CM3D2 Converter.cm3d2_data

CM3D2/COM3D2用のデータ構造を扱うデータクラス

   1"""CM3D2/COM3D2用のデータ構造を扱うデータクラス"""
   2import bpy
   3import copy
   4import struct
   5from . import common
   6from . import compat
   7from .translations.pgettext_functions import *
   8
   9SHADER_NAMES_CM3D2 = [
  10    'CM3D2/Toony_Lighted',
  11    'CM3D2/Toony_Lighted_Hair',
  12    'CM3D2/Toony_Lighted_Trans',
  13    'CM3D2/Toony_Lighted_Trans_NoZ',
  14    'CM3D2/Toony_Lighted_Outline',
  15    'CM3D2/Toony_Lighted_Outline_Trans',
  16    'CM3D2/Toony_Lighted_Hair_Outline',
  17    'CM3D2/Lighted_Trans',
  18    'CM3D2/Lighted',
  19    'Unlit/Texture',
  20    'Unlit/Transparent',
  21    'CM3D2/Mosaic',
  22    'CM3D2/Man',
  23    'Diffuse',
  24    'Transparent/Diffuse',
  25    'CM3D2_Debug/Debug_CM3D2_Normal2Color',
  26]
  27SHADER_NAMES_COM3D2 = [
  28    'CM3D2/Toony_Lighted',
  29    'CM3D2/Toony_Lighted_Hair',
  30    'CM3D2/Toony_Lighted_Trans',
  31    'CM3D2/Toony_Lighted_Trans_NoZ',
  32    'CM3D2/Toony_Lighted_Trans_NoZTest',
  33    'CM3D2/Toony_Lighted_Outline',
  34    'CM3D2/Toony_Lighted_Outline_Tex',
  35    'CM3D2/Toony_Lighted_Hair_Outline',
  36    # 'CM3D2/Toony_Lighted_Hair_Outline_Tex',
  37    'CM3D2/Toony_Lighted_Outline_Trans',
  38    'CM3D2/Toony_Lighted_Cutout_AtC',
  39    'CM3D2/Lighted_Cutout_AtC',
  40    'CM3D2/Lighted_Trans',
  41    'CM3D2/Lighted',
  42    'Unlit/Texture',
  43    'Unlit/Transparent',
  44    'CM3D2/Mosaic',
  45    'CM3D2/Man',
  46    'Diffuse',
  47    'Transparent/Diffuse',
  48    'CM3D2_Debug/Debug_CM3D2_Normal2Color',
  49]
  50TOON_TEXES = [
  51    'NoTex', 'ToonBlueA1', 'ToonBlueA2', 'ToonBrownA1', 'ToonGrayA1',
  52    'ToonGreenA1', 'ToonGreenA2', 'ToonGreenA3',
  53    'ToonOrangeA1',
  54    'ToonPinkA1', 'ToonPinkA2', 'ToonPurpleA1',
  55    'ToonRedA1', 'ToonRedA2',
  56    'ToonRedmmm1', 'ToonRedmm1', 'ToonRedm1',
  57    'ToonYellowA1', 'ToonYellowA2', 'ToonYellowA3', 'ToonYellowA4',
  58    'ToonFace',  # 'ToonFace002',
  59    'ToonSkin',  # 'ToonSkin002',
  60    'ToonBlackA1',
  61    'ToonFace_shadow',
  62    'ToonDress_shadow',
  63    'ToonSkin_Shadow',
  64    'ToonBlackMM1', 'ToonBlackM1', 'ToonGrayMM1', 'ToonGrayM1',
  65    'ToonPurpleMM1', 'ToonPurpleM1',
  66    'ToonSilverA1',
  67    'ToonDressMM_Shadow', 'ToonDressM_Shadow',
  68]
  69PROP_DESC = {
  70    '_MainTex': ["面の色を決定するテクスチャを指定。", "普段テスクチャと呼んでいるものは基本コレです。", "テクスチャパスは適当でも動きます。", "しかし、テクスチャ名はきちんと決めましょう。"],
  71    '_ToonRamp': ["暗い部分に乗算するグラデーション画像を指定します。"],
  72    '_ShadowTex': ["陰部分の面の色を決定するテクスチャを指定。", "「_ShadowRateToon」で範囲を指定します。"],
  73    '_ShadowRateToon': ["「_ShadowTex」を有効にする部分を指定します。", "黒色で有効、白色で無効。"],
  74    '_OutlineTex': ["アウトラインを表現するためのテクスチャを指定。(未確認)"],
  75    '_OutlineToonRamp': ["_OutlineTexの暗い部分に乗算するグラデーション画像を指定します。(未確認)"],
  76    '_Color': ["面の色を指定。", "白色で無効。基本的に白色で良いでしょう。"],
  77    '_ShadowColor': ["影の色を指定。白色で無効。", "別の物体に遮られてできた「影」の色です。"],
  78    '_RimColor': ["リムライトの色を指定。", "リムライトとは縁にできる光の反射のことです。"],
  79    '_OutlineColor': ["輪郭線の色を指定。", "黒にするか、テクスチャの明度を", "落としたものを指定するとより良いでしょう。"],
  80    '_Shininess': ["スペキュラーの強さを指定。0.0~1.0で指定。", "スペキュラーとは面の角度と光源の角度によって", "できるハイライトのことです。", "金属、皮、ガラスなどに使うと良いでしょう。"],
  81    '_OutlineWidth': ["輪郭線の太さを指定。", "0.002は太め、0.001は細め。"],
  82    '_RimPower': ["リムライトの強さを指定。", "この値は10以上なことも多いです。", "0に近い値だと正常に表示されません。"],
  83    '_RimShift': ["リムライトの幅を指定。", "0.0~1.0で指定。0.5でもかなり強い。"],
  84    '_RenderTex': ["モザイクシェーダーにある設定値。", "特に設定の必要なし。"],
  85    '_FloatValue1': ["モザイクの粗さ"],
  86    '_Cutoff': ["アルファのカットオフ値。", "アルファ値がこの値より大きい部分だけがレンダリングされる"],
  87    # '_Cutout': ["アルファのカットオフ値。", "アルファ値がこの値より大きい部分だけがレンダリングされる"],
  88    '_ZTest': ["デプステストの実行方法を指定する。"]
  89}
  90PROPS = {
  91    '_MainTex': {
  92        'type': 'tex',
  93        'desc': ["面の色を決定するテクスチャを指定。", "普段テスクチャと呼んでいるものは基本コレです。", "テクスチャパスは適当でも動きます。", "しかし、テクスチャ名はきちんと決めましょう。"],
  94    },
  95    '_ToonRamp': {
  96        'type': 'tex',
  97        'desc': ["暗い部分に乗算するグラデーション画像を指定します。"],
  98    },
  99    '_ShadowTex': {
 100        'type': 'tex',
 101        'desc': ["陰部分の面の色を決定するテクスチャを指定。", "「_ShadowRateToon」で範囲を指定します。"],
 102    },
 103    '_ShadowRateToon': {
 104        'type': 'tex',
 105        'desc': ["「_ShadowTex」を有効にする部分を指定します。", "黒色で有効、白色で無効。"],
 106    },
 107    '_OutlineTex': {
 108        'type': 'tex',
 109        'desc': ["アウトラインを表現するためのテクスチャを指定。"],
 110    },
 111    '_OutlineToonRamp': {
 112        'type': 'tex',
 113        'desc': ["_OutlineTexの暗い部分に乗算するグラデーション画像を指定します。"],
 114    },
 115    '_RenderTex': {
 116        'type': 'tex',
 117        'desc': ["モザイクシェーダーにある設定値。", "特に設定の必要なし。"],
 118    },
 119    '_Color': {
 120        'type': 'col',
 121        'desc': ["面の色を指定。", "白色で無効。基本的に白色で良いでしょう。"],
 122    },
 123    '_ShadowColor': {
 124        'type': 'col',
 125        'desc': ["影の色を指定。白色で無効。", "別の物体に遮られてできた「影」の色です。"],
 126    },
 127    '_RimColor': {
 128        'type': 'col',
 129        'desc': ["リムライトの色を指定。", "リムライトとは縁にできる光の反射のことです。"],
 130    },
 131    '_OutlineColor': {
 132        'type': 'col',
 133        'desc': ["輪郭線の色を指定。", "黒にするか、テクスチャの明度を", "落としたものを指定するとより良いでしょう。"],
 134    },
 135    '_Shininess': {
 136        'type': 'f',
 137        'desc': ["スペキュラーの強さを指定。0.0~1.0で指定。", "スペキュラーとは面の角度と光源の角度によって", "できるハイライトのことです。", "金属、皮、ガラスなどに使うと良いでしょう。"],
 138        'presets': [0, 0.1, 0.5, 1, 5],
 139        # 'default': 0, 'step': 1, 'precision': 2,
 140        # 'min': -100, 'soft_min': -100,
 141        # 'max': 100, 'soft_max': 100,
 142    },
 143    '_OutlineWidth': {
 144        'type': 'f',
 145        'desc': ["輪郭線の太さを指定。", "0.002は太め、0.001は細め。"],
 146        'presets': [0.0001, 0.001, 0.0015, 0.002],
 147        'dispExact': True,
 148        # 'default': 0, 'step': 0.001, 'precision': 4,
 149        # 'min': 0, 'soft_min': 1,
 150        # 'max': 0, 'soft_max': 1,
 151    },
 152    '_RimPower': {
 153        'type': 'f',
 154        'desc': ["リムライトの強さを指定。", "この値は10以上なことも多いです。", "0に近い値だと正常に表示されません。"],
 155        'presets': [0, 25, 50, 100],  # 1, 10, 20, 30
 156        # 'default': 0, 'step': 1, 'precision': 2,
 157        # 'min': -100, 'soft_min': -100,
 158        # 'max': 100, 'soft_max': 100,
 159    },
 160    '_RimShift': {
 161        'type': 'f',
 162        'desc': ["リムライトの幅を指定。", "0.0~1.0で指定。0.5でもかなり強い。"],
 163        'presets': [0, 0.25, 0.5, 0.75, 1],  # 0.0, 0.25, 0.5, 0.75, 1.0
 164        # 'default': 0, 'step': 1, 'precision': 2,
 165        # 'min': -100, 'soft_min': -100,
 166        # 'max': 100, 'soft_max': 100,
 167    },
 168    '_FloatValue1': {
 169        'type': 'f',
 170        'desc': ["モザイクの粗さ"],
 171        'presets': [0, 100, 200],
 172        # 'default': 0, 'step': 1, 'precision': 2,
 173        # 'min': -100, 'soft_min': -100,
 174        # 'max': 100, 'soft_max': 100,
 175    },
 176    '_Cutoff': {
 177        'type': 'f',
 178        'desc': ["アルファのカットオフ値。", "アルファ値がこの値より大きい部分だけがレンダリングされる"],
 179        'presets': [0, 0.1, 0.5, 1, 5],
 180        # 'default': 0, 'step': 1, 'precision': 2,
 181        # 'min': -100, 'soft_min': -100,
 182        # 'max': 100, 'soft_max': 100,
 183    },
 184    # '_Cutout': ["アルファのカットオフ値。", "アルファ値がこの値より大きい部分だけがレンダリングされる"],
 185    '_ZTest': {
 186        'type': 'f',
 187        'desc': ["デプステストの実行方法を指定する。"],
 188        'disableSlider': True,
 189        'preset_enums': [
 190            (0, "Disabled:0"), (1, "Never:1"), (2, "Less:2"), (3, "Equal:3"), (4, "LessEqual:4"),
 191            (5, "Greater:5"), (6, "NotEqual:6"), (7, "GreaterEqual:7"), (8, "Always:8")
 192        ],
 193    },
 194    '_FloatValue2': {
 195        'type': 'f',
 196        'presets': [-15, 0, 1, 15],
 197        # 'default': 0, 'step': 1, 'precision': 2,
 198        # 'min': -100, 'soft_min': -100,
 199        # 'max': 100, 'soft_max': 100,
 200    },
 201    '_FloatValue3': {
 202        'type': 'f',
 203        'presets': [0, 0.1, 0.5, 1],
 204        # 'default': 0, 'step': 1, 'precision': 2,
 205        # 'min': -100, 'soft_min': -100,
 206        # 'max': 100, 'soft_max': 100,
 207    },
 208    '_ZTest2': {
 209        'type': 'f',
 210        'disableSlider': True,
 211        'preset_enums': [(0, "0"), (1, "1")],
 212        # 'default': 0, 'step': 1, 'precision': 2,
 213        # 'min': -100, 'soft_min': -100,
 214        # 'max': 100, 'soft_max': 100,
 215    },
 216    '_ZTest2Alpha': {
 217        'type': 'f',
 218        'presets': [0, 0.8, 1],
 219        # 'default': 0, 'step': 1, 'precision': 2,
 220        # 'min': -100, 'soft_min': -100,
 221        # 'max': 100, 'soft_max': 100,
 222    }
 223}
 224
 225
 226class DataHandler:
 227    _instance = None
 228
 229    def __new__(cls):
 230        if cls._instance is None:
 231            cls._instance = super().__new__(cls)
 232        return cls._instance
 233
 234    @classmethod
 235    def instance(cls):
 236        if cls._instance is None:
 237            cls._instance = DataHandler()
 238
 239        return cls._instance
 240
 241    def __init__(self):
 242        diffuse = {
 243            'type_name': "リアル",
 244            'icon': 'BRUSH_CLAY_STRIPS',
 245            'shader2': 'Legacy Shaders__Diffuse',
 246            'tex_list': ['_MainTex'],
 247            'col_list': ['_Color'],
 248            'f_list': [],
 249        }
 250        trans_diffuse = {
 251            'type_name': "リアル 透過",
 252            'icon': 'BRUSH_TEXFILL',
 253            'shader2': 'Legacy Shaders__Transparent__Diffuse',
 254            'tex_list': ['_MainTex'],
 255            'col_list': ['_Color'],
 256            'f_list': [],
 257        }
 258
 259        self.shader_dict = {
 260            'CM3D2/Toony_Lighted': {
 261                'type_name': "トゥーン",
 262                'icon': compat.icon('SHADING_SOLID'),
 263                'shader2': 'CM3D2__Toony_Lighted',
 264                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
 265                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
 266                'f_list': ['_Shininess', '_RimPower', '_RimShift']
 267            },
 268            'CM3D2/Toony_Lighted_Hair': {
 269                'type_name': "トゥーン 髪",
 270                'icon': 'PARTICLEMODE',
 271                'shader2': 'CM3D2__Toony_Lighted_Hair',
 272                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'],
 273                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
 274                'f_list': ['_Shininess', '_RimPower', '_RimShift', '_HiRate', '_HiPow']
 275            },
 276            'CM3D2/Toony_Lighted_Trans': {
 277                'type_name': "トゥーン 透過",
 278                'icon': compat.icon('SHADING_WIRE'),
 279                'shader2': 'CM3D2__Toony_Lighted_Trans',
 280                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
 281                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
 282                'f_list': ['_Shininess', '_Cutoff', '_RimPower', '_RimShift'],
 283            },
 284            'CM3D2/Toony_Lighted_Trans_NoZ': {
 285                'type_name': "トゥーン 透過 NoZ",
 286                'icon': 'DRIVER',
 287                'shader2': 'CM3D2__Toony_Lighted_Trans_NoZ',
 288                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
 289                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
 290                'f_list': ['_Shininess', '_RimPower', '_RimShift'],
 291            },
 292            'CM3D2/Toony_Lighted_Trans_NoZTest': {
 293                'type_name': "トゥーン 透過 NoZTest",
 294                'icon': 'ANIM_DATA',
 295                'shader2': 'CM3D2__Toony_Lighted_Trans_NoZTest',
 296                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
 297                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
 298                'f_list': ['_Shininess', '_RimPower', '_RimShift', '_ZTest', '_ZTest2', '_ZTest2Alpha'],
 299            },
 300            'CM3D2/Toony_Lighted_Outline': {
 301                'type_name': "トゥーン 輪郭線",
 302                'icon': 'ANTIALIASED',
 303                'shader2': 'CM3D2__Toony_Lighted_Outline',
 304                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
 305                'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
 306                'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'],
 307            },
 308            'CM3D2/Toony_Lighted_Outline_Tex': {
 309                'type_name': "トゥーン 輪郭線 Tex",
 310                'icon': 'MATSPHERE',
 311                'shader2': 'CM3D2__Toony_Lighted_Outline_Tex',
 312                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_OutlineTex', '_OutlineToonRamp'],
 313                'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
 314                'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'],
 315            },
 316            'CM3D2/Toony_Lighted_Hair_Outline': {
 317                'type_name': "トゥーン 輪郭線 髪",
 318                'icon': 'PARTICLEMODE',
 319                'shader2': 'CM3D2__Toony_Lighted_Hair_Outline',
 320                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'],
 321                'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
 322                'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift', '_HiRate', '_HiPow'],
 323            },
 324            # 'CM3D2/Toony_Lighted_Hair_Outline_Tex': {
 325            # 	'type_name': "トゥーン 輪郭線 Tex 髪",
 326            # 	'icon': 'PARTICLEMODE',
 327            # 	'shader2': 'CM3D2__Toony_Lighted_Hair_Outline_Tex',
 328            # 	'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'],
 329            # 	'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
 330            # 	'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift', '_HiRate', '_HiPow'],
 331            # },
 332            'CM3D2/Toony_Lighted_Outline_Trans': {
 333                'type_name': "トゥーン 輪郭線 透過",
 334                'icon': 'PROP_OFF',
 335                'shader2': 'CM3D2__Toony_Lighted_Outline_Trans',
 336                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
 337                'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
 338                'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'],
 339            },
 340            'CM3D2/Toony_Lighted_Cutout_AtC': {
 341                'type_name': "トゥーン Cutout",
 342                'icon': 'IPO_BACK',
 343                'shader2': 'CM3D2__Toony_Lighted_Cutout_AtC',
 344                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
 345                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
 346                'f_list': ['_Shininess', '_RimPower', '_RimShift', '_Cutoff'],
 347            },
 348            'CM3D2/Lighted_Trans': {
 349                'type_name': "トゥーン無し 透過",
 350                'icon': compat.icon('VIS_SEL_01'),
 351                'shader2': 'CM3D2__Lighted_Trans',
 352                'tex_list': ['_MainTex'],
 353                'col_list': ['_Color', '_ShadowColor'],
 354                'f_list': ['_Shininess'],
 355            },
 356            'CM3D2/Lighted': {
 357                'type_name': "トゥーン無し",
 358                'icon': compat.icon('VIS_SEL_11'),
 359                'shader2': 'CM3D2__Lighted',
 360                'tex_list': ['_MainTex'],
 361                'col_list': ['_Color', '_ShadowColor'],
 362                'f_list': ['_Shininess'],
 363            },
 364            'CM3D2/Lighted_Cutout_AtC': {
 365                'type_name': "トゥーン無し Cutout",
 366                'icon': 'IPO_BACK',
 367                'shader2': 'CM3D2__Lighted_Cutout_AtC',
 368                'tex_list': ['_MainTex'],
 369                'col_list': ['_Color', '_ShadowColor'],
 370                'f_list': ['_Shininess', '_Cutoff'],
 371            },
 372            'Unlit/Texture': {
 373                'type_name': "発光",
 374                'icon': 'PARTICLES',
 375                'shader2': 'Unlit__Texture',
 376                'tex_list': ['_MainTex'],
 377                'col_list': [],  # ['_Color'],
 378                'f_list': [],
 379            },
 380            'Unlit/Transparent': {
 381                'type_name': "発光 透過",
 382                'icon': 'MOD_PARTICLES',
 383                'shader2': 'Unlit__Texture',
 384                'tex_list': ['_MainTex'],
 385                'col_list': [],  # ['_Color'],
 386                'f_list': [],
 387            },
 388            'CM3D2/Mosaic': {
 389                'type_name': "モザイク",
 390                'icon': 'ALIASED',
 391                'shader2': 'CM3D2__Mosaic',
 392                'tex_list': ['_RenderTex'],
 393                'col_list': [],
 394                'f_list': ['_FloatValue1'],
 395            },
 396            'CM3D2/Man': {
 397                'type_name': "ご主人様",
 398                'icon': 'ARMATURE_DATA',
 399                'shader2': 'CM3D2__Man',
 400                'tex_list': [],
 401                'col_list': ['_Color'],
 402                'f_list': ['_FloatValue2', '_FloatValue3'],
 403            },
 404            'Diffuse': diffuse,
 405            'Legacy Shaders/Diffuse': diffuse,
 406            'Transparent/Diffuse': trans_diffuse,
 407            'Legacy Shaders/Transparent/Diffuse': trans_diffuse,
 408            'CM3D2_Debug/Debug_CM3D2_Normal2Color': {
 409                'type_name': "法線",
 410                'icon': compat.icon('NORMALS_VERTEX'),
 411                'shader2': 'CM3D2_Debug__Debug_CM3D2_Normal2Color',
 412                'tex_list': [],
 413                'col_list': ['_Color'],  # , '_RimColor', '_OutlineColor', '_SpecColor'],
 414                'f_list': []  # ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'],
 415            },
 416        }
 417
 418    @classmethod
 419    def create_shader_items(cls) -> list:
 420        _inst = cls.instance()
 421        items = []
 422        idx = 0
 423        for name in SHADER_NAMES_CM3D2:
 424            item = _inst.shader_dict.get(name)
 425            if item:
 426                items.append((name, item['type_name'], '', item['icon'], idx))
 427                idx += 1
 428        return items
 429
 430    @classmethod
 431    def create_comshader_items(cls) -> list:
 432        _inst = cls.instance()
 433        items = []
 434        idx = 0
 435        for name in SHADER_NAMES_COM3D2:
 436            item = _inst.shader_dict.get(name)
 437            if item:
 438                items.append((name, item['type_name'], '', item['icon'], idx))
 439                idx += 1
 440        return items
 441
 442    @classmethod
 443    def get_shader_prop(cls, name):
 444        _inst = cls.instance()
 445        shader_prop = _inst.shader_dict.get(name)
 446        if shader_prop:
 447            return shader_prop
 448
 449        return {'type_name': '不明', 'icon': 'NONE'}
 450
 451Handler = DataHandler.instance()
 452
 453
 454class Material():
 455    """マテリアルデータクラス"""
 456    def __init__(self):
 457        self.version = 1000
 458        self.name1 = None
 459        self.name2 = None
 460        self.shader1 = None
 461        self.shader2 = None
 462
 463        self.tex_list = []  # prop_name, (tex_name, tex_path, trans[2], scale[2])
 464        self.col_list = []  # prop_name, col[4]
 465        self.f_list = []  # prop_name, f
 466        self.range_list = [] # prop_name, col[4]
 467
 468        self.custom_list = dict()
 469
 470    def sort(self):
 471        self.tex_list = sorted(self.tex_list, key=lambda item: item[0])
 472        self.col_list = sorted(self.col_list, key=lambda item: item[0])
 473        self.f_list = sorted(self.f_list, key=lambda item: item[0])
 474
 475    @property
 476    def name(self):
 477        return self.name2 or self.name1
 478
 479    def read(self, reader, read_header=True):
 480        if read_header:
 481            header = common.read_str(reader)
 482            if header != 'CM3D2_MATERIAL':
 483                raise common.CM3D2ImportException(f_tip_("mateファイルではありません。ヘッダ:{}", header))
 484            self.version = struct.unpack('<i', reader.read(4))[0]
 485            self.name1 = common.read_str(reader)
 486        self.name2 = common.read_str(reader)
 487
 488        self.shader1 = common.read_str(reader)
 489        self.shader2 = common.read_str(reader)
 490        
 491        peeked = reader.peek()[0]
 492        print(f"type({peeked}) = {type(peeked)}")
 493        if self.version >= 2102 and (peeked in (0, 1)): # CR Edit Mode
 494            cr_unknown_float_count = struct.unpack('<B', reader.read(1))[0]
 495            for i in range(cr_unknown_float_count):
 496                # CR TODO
 497                self.custom_list[f'cr_unknown_float:{i:03d}'] = struct.unpack('<f', reader.read(4))
 498
 499        for i in range(99999):
 500            prop_type = common.read_str(reader)
 501            if prop_type == 'tex':
 502                prop_name = common.read_str(reader)
 503                sub_type = common.read_str(reader)
 504                if sub_type == 'tex2d':
 505                    tex_name = common.read_str(reader)
 506                    tex_path = common.read_str(reader)
 507                    offset = struct.unpack('<2f', reader.read(4 * 2))
 508                    scale = struct.unpack('<2f', reader.read(4 * 2))
 509                    tex_item = [prop_name, tex_name, tex_path, offset, scale]
 510                else:
 511                    tex_item = [prop_name]
 512                self.tex_list.append(tex_item)
 513
 514            elif prop_type == 'col':
 515                prop_name = common.read_str(reader)
 516                col = struct.unpack('<4f', reader.read(4 * 4))
 517                self.col_list.append([prop_name, col])
 518
 519            elif prop_type == 'f' or prop_type == 'range': # 'range' from CR Edit
 520                prop_name = common.read_str(reader)
 521                f = struct.unpack('<f', reader.read(4))[0]
 522                self.f_list.append([prop_name, f])
 523            
 524            # CR TODO
 525            elif prop_type == 'keyword':
 526                prop_name = common.read_str(reader)
 527                keyword_f = struct.unpack('<f', reader.read(4))[0]
 528                print(keyword_f, type(keyword_f))
 529                self.custom_list.setdefault('keyword', dict())[prop_name] = keyword_f #.append([prop_name, keyword_f])
 530                
 531            # CR TODO
 532            elif prop_type == '_ALPHAPREMULTIPLY_ON':
 533                alpha_bool = struct.unpack('<?', reader.read(1))[0]
 534                self.custom_list['_ALPHAPREMULTIPLY_ON'] = alpha_bool
 535
 536            elif prop_type == 'end':
 537                break
 538            else:
 539                raise common.CM3D2ImportException(f_tip_("Materialプロパティに未知の設定値タイプ({prop})が見つかりました。", prop=prop_type))
 540
 541    def write(self, writer, write_header=True):
 542        if write_header:
 543            common.write_str(writer, 'CM3D2_MATERIAL')
 544            writer.write(struct.pack('<i', self.version))
 545            common.write_str(writer, self.name1)
 546
 547        common.write_str(writer, self.name2)
 548        common.write_str(writer, self.shader1)
 549        common.write_str(writer, self.shader2)
 550
 551        for tex_item in self.tex_list:
 552            common.write_str(writer, 'tex')
 553            common.write_str(writer, tex_item[0])  # prop_name
 554
 555            if len(tex_item) < 2:
 556                common.write_str(writer, 'null')
 557            else:
 558                common.write_str(writer, 'tex2d')
 559                common.write_str(writer, tex_item[1])  # tex_name
 560                common.write_str(writer, tex_item[2])  # tex_path
 561                trans = tex_item[3]
 562                writer.write(struct.pack('<2f', trans[0], trans[1]))
 563                scale = tex_item[4]
 564                writer.write(struct.pack('<2f', scale[0], scale[1]))
 565
 566        for col_item in self.col_list:
 567            common.write_str(writer, 'col')
 568            common.write_str(writer, col_item[0])  # prop_name
 569
 570            col = col_item[1]
 571            writer.write(struct.pack('<4f', col[0], col[1], col[2], col[3]))
 572
 573        for f_item in self.f_list:
 574            common.write_str(writer, 'f')
 575            common.write_str(writer, f_item[0])  # prop_name
 576
 577            writer.write(struct.pack('<f', f_item[1]))
 578
 579        common.write_str(writer, 'end')
 580
 581    def to_text(self):
 582        output_text = str(self.version) + "\n"
 583        output_text += self.name1 + "\n"
 584        output_text += self.name2 + "\n"
 585        output_text += self.shader1 + "\n"
 586        output_text += self.shader2 + "\n"
 587        output_text += "\n"
 588
 589        for tex_item in self.tex_list:
 590            output_text += 'tex\n'
 591            output_text += "\t" + tex_item[0] + "\n"  # prop_name
 592
 593            if len(tex_item) < 2:
 594                output_text += '\tnull\n'
 595            else:
 596                output_text += '\ttex2d\n'
 597                output_text += "\t" + tex_item[1] + "\n"  # tex_name
 598                output_text += "\t" + tex_item[2] + "\n"  # tex_path
 599                trans = tex_item[3]
 600                scale = tex_item[4]
 601                output_text += "\t" + " ".join([str(trans[0]), str(trans[1]), str(scale[0]), str(scale[1])]) + "\n"
 602
 603        for col_item in self.col_list:
 604            output_text += 'col\n'
 605            output_text += "\t" + col_item[0] + "\n"  # prop_name
 606            col = col_item[1]
 607            output_text += "\t" + " ".join([str(col[0]), str(col[1]), str(col[2]), str(col[3])]) + "\n"  # prop_name
 608
 609        for f_item in self.f_list:
 610            output_text += 'f\n'
 611            output_text += "\t" + f_item[0] + "\n"  # prop_name
 612            f = f_item[1]
 613            output_text += "\t" + str(f) + "\n"
 614
 615        return output_text
 616
 617    def to_json(self):
 618        import json
 619        return json.dumps(self.__dict__, ensure_ascii=False, indent=2)
 620
 621    def from_dict(self, data):
 622        self.name1 = data['name1']
 623        self.name2 = data['name2']
 624        self.version = data['version']
 625        self.shader1 = data['shader1']
 626        self.shader2 = data['shader2']
 627
 628        self.tex_list = data['tex_list']  # prop_name, (tex_name, tex_path, trans[2], scale[2])
 629        self.col_list = data['col_list']  # prop_name, col[4]
 630        self.f_list = data['f_list']  # prop_name, f
 631
 632
 633class MaterialHandler:
 634
 635    @classmethod
 636    def parse_tex_node(cls, node, remove_serial=True):
 637        node_name = common.remove_serial_number(node.name, remove_serial)
 638        tex_item = [node_name]
 639
 640        try:
 641            img = node.image
 642        except:
 643            raise common.CM3D2ImportException('Materialプロパティのtexタイプの設定値取得に失敗しました。')
 644
 645        if img:
 646            tex_name = common.remove_serial_number(img.name, remove_serial)
 647            tex_name = common.re_png.sub(r'\1', tex_name)  # 拡張子を除外
 648            tex_item.append(tex_name)
 649
 650            if 'cm3d2_path' in img:
 651                path = img['cm3d2_path']
 652            else:
 653                path = bpy.path.abspath(img.filepath)
 654            path = common.to_cm3d2path(path)
 655
 656            tex_item.append(path)
 657            tex_map = node.texture_mapping
 658            tex_trans = tex_map.translation[:2]
 659            tex_scale = tex_map.scale[:2]
 660            tex_item.append(tex_trans)
 661            tex_item.append(tex_scale)
 662        return tex_item
 663
 664    @classmethod
 665    def parse_col_node(cls, node, remove_serial=True):
 666        node_name = common.remove_serial_number(node.name, remove_serial)
 667        col = node.outputs[0].default_value
 668        return [node_name, col[:4]]
 669
 670    @classmethod
 671    def parse_f_node(cls, node, remove_serial=True):
 672        node_name = common.remove_serial_number(node.name, remove_serial)
 673        f = node.outputs[0].default_value
 674        return [node_name, f]
 675
 676    @classmethod
 677    def read(cls, reader, read_header=True, version=None):
 678        if not read_header and version == None:
 679            raise ValueError(f_tip_("The argument 'version' is required when 'read_header' is False for MaterialHandler.read()"))
 680        mat_data = Material()
 681        if version:
 682            mat_data.version = version
 683        mat_data.read(reader, read_header)
 684
 685        return mat_data
 686
 687    @classmethod
 688    def get_shader_prop_dynamic(cls, mate, ):
 689        lists = { 'VALUE':'f_list', 'RGB':'col_list', 'TEX_IMAGE':'tex_list' }
 690        shader_prop = copy.deepcopy( DataHandler.get_shader_prop(mate.get('shader1')) )
 691        for node in mate.node_tree.nodes:
 692            if node.name[0] != '_':
 693                continue
 694            list_name = lists.get(node.type)
 695            if not list_name:
 696                continue
 697            prop_list = shader_prop.get(list_name)
 698            if not prop_list:
 699                prop_list = list()
 700                shader_prop[list_name] = prop_list
 701            if node.name in prop_list:
 702                continue
 703            prop_list.append(node.name)
 704        return shader_prop
 705
 706    @classmethod
 707    def parse_mate(cls, mate, remove_serial=True):
 708        mat_data = Material()
 709
 710        mate_name = common.remove_serial_number(mate.name, remove_serial)
 711        mat_data.name1 = mate_name.lower()
 712        mat_data.name2 = mate_name
 713        mat_data.shader1 = mate['shader1']
 714        mat_data.shader2 = mate['shader2']
 715
 716        nodes = mate.node_tree.nodes
 717        shader_prop = cls.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(mat_data.shader1)
 718        if shader_prop:
 719            for node_name in shader_prop['tex_list']:
 720                node = nodes.get(node_name)
 721                if node and node.type == 'TEX_IMAGE':
 722                    tex_item = cls.parse_tex_node(node, remove_serial)
 723                    mat_data.tex_list.append(tex_item)
 724
 725            for node_name in shader_prop['col_list']:
 726                node = nodes.get(node_name)
 727                if node and node.type == 'RGB':
 728                    col_item = cls.parse_col_node(node, remove_serial)
 729                    mat_data.col_list.append(col_item)
 730
 731            for node_name in shader_prop['f_list']:
 732                node = nodes.get(node_name)
 733                if node and node.type == 'VALUE':
 734                    f_item = cls.parse_f_node(node, remove_serial)
 735                    mat_data.f_list.append(f_item)
 736
 737        #for node in nodes:
 738        #    if not node.name.startswith('_'):
 739        #        continue
 740        #
 741        #    node_type = node.type
 742        #    if node_type == 'TEX_IMAGE':
 743        #        tex_item = cls.parse_tex_node(node, remove_serial)
 744        #        mat_data.tex_list.append(tex_item)
 745        #    elif node_type == 'RGB':
 746        #        col_item = cls.parse_col_node(node, remove_serial)
 747        #        mat_data.col_list.append(col_item)
 748        #
 749        #    elif node_type == 'VALUE':
 750        #        f_item = cls.parse_f_node(node, remove_serial)
 751        #        mat_data.f_list.append(f_item)
 752        
 753        return mat_data
 754
 755    @classmethod
 756    def parse_mate_old(cls, mate, remove_serial=True):
 757        """material parser for blender-2.7x"""
 758        mat_data = Material()
 759
 760        mate_name = common.remove_serial_number(mate.name, remove_serial)
 761        mat_data.name1 = mate_name.lower()
 762        mat_data.name2 = mate_name
 763        mat_data.shader1 = mate['shader1']
 764        mat_data.shader2 = mate['shader2']
 765
 766        for tindex, tslot in enumerate(mate.texture_slots):
 767            if not tslot:
 768                continue
 769
 770            tex = tslot.texture
 771            node_name = common.remove_serial_number(tex.name, remove_serial)
 772            # node_name = tslot.name
 773            if mate.use_textures[tindex]:
 774                tex_item = [node_name]
 775                try:
 776                    img = tex.image
 777                except:
 778                    raise Exception('Materialプロパティのtexタイプの設定値取得に失敗しました。')
 779
 780                if img:
 781                    tex_name = common.remove_serial_number(img.name, remove_serial)
 782                    tex_name = common.re_png.sub(r'\1', tex_name)  # 拡張子を除外
 783                    tex_item.append(tex_name)
 784
 785                    if 'cm3d2_path' in img:
 786                        path = img['cm3d2_path']
 787                    else:
 788                        path = bpy.path.abspath(img.filepath)
 789                    path = common.to_cm3d2path(path)
 790
 791                    tex_item.append(path)
 792                    tex_trans = tex.offset[:2]
 793                    tex_scale = tex.scale[:2]
 794                    tex_item.append(tex_trans)
 795                    tex_item.append(tex_scale)
 796
 797                mat_data.tex_list.append(tex_item)
 798            elif tslot.use_rgb_to_intensity:
 799                col = tex.color[:3] + [tslot.diffuse_color_factor]
 800                mat_data.col_list.append([node_name, col[:4]])
 801            else:
 802                f = tslot.diffuse_color_factor
 803                mat_data.f_list.append([node_name, f])
 804
 805        return mat_data
 806
 807    @classmethod
 808    def parse_text(cls, text):
 809        mat_data = Material()
 810        lines = text.split('\n')
 811
 812        mat_data.version = int(lines[0])
 813        mat_data.name1 = lines[1]
 814        mat_data.name2 = lines[2]
 815        mat_data.shader1 = lines[3]
 816        mat_data.shader2 = lines[4]
 817
 818        line_seek = 5
 819        while line_seek < len(lines):
 820            node_type = common.line_trim(lines[line_seek])
 821            if not node_type:
 822                line_seek += 1
 823                continue
 824            if node_type == 'tex':
 825                prop_name = common.line_trim(lines[line_seek + 1])
 826                sub_type = common.line_trim(lines[line_seek + 2])
 827                if sub_type == 'tex2d':
 828                    line_seek += 3
 829                    tex_name = common.line_trim(lines[line_seek])
 830                    tex_path = common.line_trim(lines[line_seek + 1])
 831                    tex_map = common.line_trim(lines[line_seek + 2]).split(' ')
 832                    for map_datum in range(len(tex_map)):
 833                        tex_map[map_datum] = float(tex_map[map_datum])
 834                    mat_data.tex_list.append([prop_name, tex_name, tex_path, tex_map[:2], tex_map[2:]])
 835                else:
 836                    mat_data.tex_list.append([prop_name])
 837
 838                line_seek += 3
 839
 840            elif node_type == 'col':
 841                prop_name = common.line_trim(lines[line_seek + 1])
 842                tex_map = common.line_trim(lines[line_seek + 2]).split(' ')
 843                for map_datum in range(len(tex_map)):
 844                    tex_map[map_datum] = float(tex_map[map_datum])
 845
 846                mat_data.col_list.append([prop_name, tex_map[:]])
 847                line_seek += 3
 848
 849            elif node_type == 'f':
 850                prop_name = common.line_trim(lines[line_seek + 1])
 851                val = float(common.line_trim(lines[line_seek + 2]))
 852
 853                mat_data.f_list.append([prop_name, val])
 854                line_seek += 3
 855            else:
 856                raise Exception('未知の設定値タイプが見つかりました。')
 857
 858        return mat_data
 859
 860    @classmethod
 861    def parse_json(cls, text):
 862        import json
 863        mat_data = Material()
 864        mat_data.from_dict(json.loads(text))
 865
 866        return mat_data
 867
 868    @classmethod
 869    def apply_to(cls, override, mate, mat_data, replace_tex=True):
 870        mate['shader1'] = mat_data.shader1
 871        mate['shader2'] = mat_data.shader2
 872
 873        if mate.use_nodes is False:
 874            mate.use_nodes = True
 875
 876        nodes = mate.node_tree.nodes
 877        nodes.clear()
 878        # OUTPUT_MATERIAL, BSDF_PRINCIPLEDは消さない
 879        #if len(nodes) > 2:
 880        #    clear_nodes(nodes)
 881
 882        for tex_item in mat_data.tex_list:
 883            prop_name = tex_item[0]
 884
 885            if len(tex_item) < 2:
 886                common.create_tex(override, mate, prop_name)
 887            else:
 888                tex_name = tex_item[1]
 889                tex_path = tex_item[2]
 890                tex_map = tex_item[3] + tex_item[4]
 891                common.create_tex(override, mate, prop_name, tex_name, tex_path, tex_path, tex_map, replace_tex)
 892
 893        for col_item in mat_data.col_list:
 894            prop_name = col_item[0]
 895            col = col_item[1]
 896            common.create_col(override, mate, prop_name, col)
 897
 898        for item in mat_data.f_list:
 899            prop_name = item[0]
 900            f = item[1]
 901            common.create_float(override, mate, prop_name, f)
 902
 903        for key, value in mat_data.custom_list.items():
 904            mate[key] = value
 905            if type(value) == bool:
 906                rna_ui = mate.get('_RNA_UI', dict())
 907                rna_ui[key] = {
 908                    "default"  : value,
 909                    "min"      : 0    ,
 910                    "max"      : 1    ,
 911                    "soft_min" : 0    ,
 912                    "soft_max" : 1    ,
 913                }
 914                mate['_RNA_UI'] = rna_ui
 915
 916
 917
 918        align_nodes(mate)
 919
 920    @classmethod
 921    def apply_to_old(cls, override, mate, mat_data, replace_tex=True, decorate=True, skip_same_prop=True):
 922        ob = override['active_object']
 923        me = ob.data
 924
 925        mate['shader1'] = mat_data.shader1
 926        mate['shader2'] = mat_data.shader2
 927
 928        slot_index = 0
 929        olds_slots = {}
 930        read_texes = set() if skip_same_prop else None
 931
 932        for item in mat_data.tex_list:
 933            prop_name = item[0]
 934            if read_texes:
 935                if prop_name in read_texes:
 936                    continue
 937                read_texes.add(prop_name)
 938
 939            slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'IMAGE')
 940            slot.use_rgb_to_intensity = False
 941            mate.use_textures[slot_index] = True
 942
 943            if len(item) > 4:
 944                tex = slot.texture
 945                tex_name = item[1]
 946                tex_path = item[2]
 947                if tex_name in read_texes:
 948                    continue
 949
 950                if tex_name != common.remove_serial_number(tex.image.name):
 951                    tex.image.name = tex_name
 952                tex.image['cm3d2_path'] = tex_path
 953                tex.image.filepath = tex.image['cm3d2_path']
 954
 955                slot.offset = item[3]
 956                slot.scale = item[4]
 957                if replace_tex:
 958                    if common.replace_cm3d2_tex(tex.image, reload_path=True) and prop_name == '_MainTex':
 959                        for face in me.polygons:
 960                            if face.material_index == ob.active_material_index:
 961                                me.uv_textures.active.data[face.index].image = tex.image
 962            slot_index += 1
 963
 964        for item in mat_data.col_list:
 965            prop_name = item[0]
 966            col = item[1]
 967
 968            slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND')
 969
 970            mate.use_textures[slot_index] = False
 971            slot.use_rgb_to_intensity = True
 972            slot.color = col[:3]
 973            slot.diffuse_color_factor = col[3]
 974
 975            slot_index += 1
 976
 977        for item in mat_data.f_list:
 978            prop_name = item[0]
 979            f = item[1]
 980
 981            slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND')
 982
 983            mate.use_textures[slot_index] = False
 984            slot.use_rgb_to_intensity = False
 985            slot.diffuse_color_factor = f
 986
 987            slot_index += 1
 988
 989        # 存在しないスロットをクリア
 990        for item_index in range(slot_index, len(mate.texture_slots)):
 991            if mate.texture_slots[item_index]:
 992                mate.texture_slots.clear(item_index)
 993
 994        # プレビューへの反映
 995        for slot in mate.texture_slots:
 996            if slot:
 997                common.set_texture_color(slot)
 998
 999        if decorate:
1000            common.decorate_material(mate, decorate, me, ob.active_material_index)
1001
1002    @staticmethod
1003    def search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, tex_type):
1004        tex = None
1005        slot_item = mate.texture_slots[slot_index]
1006        slot_name = slot_item.name if slot_item else ''
1007
1008        slot_name = common.remove_serial_number(slot_name)
1009        # 指定スロットが同名であればそのスロットをそのまま利用する
1010        if prop_name == slot_name:
1011            slot = slot_item
1012        else:
1013            # スロット名が異なり、既にスロットがある場合はキャッシュに格納
1014            if slot_item:
1015                olds_slots[slot_name] = slot_item
1016            slot = mate.texture_slots.create(slot_index)
1017
1018            if prop_name in olds_slots:
1019                tex = olds_slots.pop(prop_name).texture
1020            else:
1021                for item_index in range(slot_index + 1, len(mate.texture_slots)):
1022                    slot_item = mate.texture_slots[item_index]
1023                    if slot_item is None:
1024                        break
1025                    if prop_name == common.remove_serial_number(slot_item.name):
1026                        tex = slot_item.texture
1027                        break
1028            if tex is None:
1029                tex = override['blend_data'].textures.new(prop_name, tex_type)
1030            slot.texture = tex
1031        return slot
1032
1033
1034def clear_nodes(nodes):
1035    for node in nodes:
1036        if node.type not in ['VALUE', 'RGB', 'TEX_IMAGE']:
1037            nodes.remove(node)
1038
1039
1040def align_nodes(mate):
1041    nodes = mate.node_tree.nodes
1042    # Principled BSDFがある前提での整列
1043    bsdf = nodes.get('Principled BSDF')
1044    base_location = (10, 300)
1045    if bsdf:
1046        main_tex = nodes.get('_MainTex')
1047        if main_tex:
1048            mate.node_tree.links.new(bsdf.inputs['Base Color'], main_tex.outputs['Color'])
1049            mate.node_tree.links.new(bsdf.inputs['Alpha'], main_tex.outputs['Alpha'])
1050        shininess = nodes.get('_Shininess')
1051        if shininess:
1052            mate.node_tree.links.new(bsdf.inputs['Specular'], shininess.outputs[0])
1053        base_location = bsdf.location
1054
1055    shader_name = mate.get('shader1')
1056    if shader_name:
1057        location_x = base_location[0] - 400
1058        location_y = base_location[1] + 60
1059        shader_prop = MaterialHandler.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(shader_name)
1060        node_list = shader_prop.get('tex_list')
1061        if node_list:
1062            for node_name in node_list:
1063                node = nodes.get(node_name)
1064                if node:
1065                    node.location = (location_x, location_y)
1066                    node.hide = True
1067                    location_y -= 60
1068
1069        col_list = shader_prop.get('col_list')
1070        if col_list:
1071            for node_name in col_list:
1072                node = nodes.get(node_name)
1073                if node:
1074                    node.location = (location_x, location_y)
1075                    location_y -= 200
1076
1077        f_list = shader_prop.get('f_list')
1078        if f_list:
1079            for node_name in f_list:
1080                node = nodes.get(node_name)
1081                if node:
1082                    node.location = (location_x, location_y)
1083                    location_y -= 90
SHADER_NAMES_CM3D2 = ['CM3D2/Toony_Lighted', 'CM3D2/Toony_Lighted_Hair', 'CM3D2/Toony_Lighted_Trans', 'CM3D2/Toony_Lighted_Trans_NoZ', 'CM3D2/Toony_Lighted_Outline', 'CM3D2/Toony_Lighted_Outline_Trans', 'CM3D2/Toony_Lighted_Hair_Outline', 'CM3D2/Lighted_Trans', 'CM3D2/Lighted', 'Unlit/Texture', 'Unlit/Transparent', 'CM3D2/Mosaic', 'CM3D2/Man', 'Diffuse', 'Transparent/Diffuse', 'CM3D2_Debug/Debug_CM3D2_Normal2Color']
SHADER_NAMES_COM3D2 = ['CM3D2/Toony_Lighted', 'CM3D2/Toony_Lighted_Hair', 'CM3D2/Toony_Lighted_Trans', 'CM3D2/Toony_Lighted_Trans_NoZ', 'CM3D2/Toony_Lighted_Trans_NoZTest', 'CM3D2/Toony_Lighted_Outline', 'CM3D2/Toony_Lighted_Outline_Tex', 'CM3D2/Toony_Lighted_Hair_Outline', 'CM3D2/Toony_Lighted_Outline_Trans', 'CM3D2/Toony_Lighted_Cutout_AtC', 'CM3D2/Lighted_Cutout_AtC', 'CM3D2/Lighted_Trans', 'CM3D2/Lighted', 'Unlit/Texture', 'Unlit/Transparent', 'CM3D2/Mosaic', 'CM3D2/Man', 'Diffuse', 'Transparent/Diffuse', 'CM3D2_Debug/Debug_CM3D2_Normal2Color']
TOON_TEXES = ['NoTex', 'ToonBlueA1', 'ToonBlueA2', 'ToonBrownA1', 'ToonGrayA1', 'ToonGreenA1', 'ToonGreenA2', 'ToonGreenA3', 'ToonOrangeA1', 'ToonPinkA1', 'ToonPinkA2', 'ToonPurpleA1', 'ToonRedA1', 'ToonRedA2', 'ToonRedmmm1', 'ToonRedmm1', 'ToonRedm1', 'ToonYellowA1', 'ToonYellowA2', 'ToonYellowA3', 'ToonYellowA4', 'ToonFace', 'ToonSkin', 'ToonBlackA1', 'ToonFace_shadow', 'ToonDress_shadow', 'ToonSkin_Shadow', 'ToonBlackMM1', 'ToonBlackM1', 'ToonGrayMM1', 'ToonGrayM1', 'ToonPurpleMM1', 'ToonPurpleM1', 'ToonSilverA1', 'ToonDressMM_Shadow', 'ToonDressM_Shadow']
PROP_DESC = {'_MainTex': ['面の色を決定するテクスチャを指定。', '普段テスクチャと呼んでいるものは基本コレです。', 'テクスチャパスは適当でも動きます。', 'しかし、テクスチャ名はきちんと決めましょう。'], '_ToonRamp': ['暗い部分に乗算するグラデーション画像を指定します。'], '_ShadowTex': ['陰部分の面の色を決定するテクスチャを指定。', '「_ShadowRateToon」で範囲を指定します。'], '_ShadowRateToon': ['「_ShadowTex」を有効にする部分を指定します。', '黒色で有効、白色で無効。'], '_OutlineTex': ['アウトラインを表現するためのテクスチャを指定。(未確認)'], '_OutlineToonRamp': ['_OutlineTexの暗い部分に乗算するグラデーション画像を指定します。(未確認)'], '_Color': ['面の色を指定。', '白色で無効。基本的に白色で良いでしょう。'], '_ShadowColor': ['影の色を指定。白色で無効。', '別の物体に遮られてできた「影」の色です。'], '_RimColor': ['リムライトの色を指定。', 'リムライトとは縁にできる光の反射のことです。'], '_OutlineColor': ['輪郭線の色を指定。', '黒にするか、テクスチャの明度を', '落としたものを指定するとより良いでしょう。'], '_Shininess': ['スペキュラーの強さを指定。0.0~1.0で指定。', 'スペキュラーとは面の角度と光源の角度によって', 'できるハイライトのことです。', '金属、皮、ガラスなどに使うと良いでしょう。'], '_OutlineWidth': ['輪郭線の太さを指定。', '0.002は太め、0.001は細め。'], '_RimPower': ['リムライトの強さを指定。', 'この値は10以上なことも多いです。', '0に近い値だと正常に表示されません。'], '_RimShift': ['リムライトの幅を指定。', '0.0~1.0で指定。0.5でもかなり強い。'], '_RenderTex': ['モザイクシェーダーにある設定値。', '特に設定の必要なし。'], '_FloatValue1': ['モザイクの粗さ'], '_Cutoff': ['アルファのカットオフ値。', 'アルファ値がこの値より大きい部分だけがレンダリングされる'], '_ZTest': ['デプステストの実行方法を指定する。']}
PROPS = {'_MainTex': {'type': 'tex', 'desc': ['面の色を決定するテクスチャを指定。', '普段テスクチャと呼んでいるものは基本コレです。', 'テクスチャパスは適当でも動きます。', 'しかし、テクスチャ名はきちんと決めましょう。']}, '_ToonRamp': {'type': 'tex', 'desc': ['暗い部分に乗算するグラデーション画像を指定します。']}, '_ShadowTex': {'type': 'tex', 'desc': ['陰部分の面の色を決定するテクスチャを指定。', '「_ShadowRateToon」で範囲を指定します。']}, '_ShadowRateToon': {'type': 'tex', 'desc': ['「_ShadowTex」を有効にする部分を指定します。', '黒色で有効、白色で無効。']}, '_OutlineTex': {'type': 'tex', 'desc': ['アウトラインを表現するためのテクスチャを指定。']}, '_OutlineToonRamp': {'type': 'tex', 'desc': ['_OutlineTexの暗い部分に乗算するグラデーション画像を指定します。']}, '_RenderTex': {'type': 'tex', 'desc': ['モザイクシェーダーにある設定値。', '特に設定の必要なし。']}, '_Color': {'type': 'col', 'desc': ['面の色を指定。', '白色で無効。基本的に白色で良いでしょう。']}, '_ShadowColor': {'type': 'col', 'desc': ['影の色を指定。白色で無効。', '別の物体に遮られてできた「影」の色です。']}, '_RimColor': {'type': 'col', 'desc': ['リムライトの色を指定。', 'リムライトとは縁にできる光の反射のことです。']}, '_OutlineColor': {'type': 'col', 'desc': ['輪郭線の色を指定。', '黒にするか、テクスチャの明度を', '落としたものを指定するとより良いでしょう。']}, '_Shininess': {'type': 'f', 'desc': ['スペキュラーの強さを指定。0.0~1.0で指定。', 'スペキュラーとは面の角度と光源の角度によって', 'できるハイライトのことです。', '金属、皮、ガラスなどに使うと良いでしょう。'], 'presets': [0, 0.1, 0.5, 1, 5]}, '_OutlineWidth': {'type': 'f', 'desc': ['輪郭線の太さを指定。', '0.002は太め、0.001は細め。'], 'presets': [0.0001, 0.001, 0.0015, 0.002], 'dispExact': True}, '_RimPower': {'type': 'f', 'desc': ['リムライトの強さを指定。', 'この値は10以上なことも多いです。', '0に近い値だと正常に表示されません。'], 'presets': [0, 25, 50, 100]}, '_RimShift': {'type': 'f', 'desc': ['リムライトの幅を指定。', '0.0~1.0で指定。0.5でもかなり強い。'], 'presets': [0, 0.25, 0.5, 0.75, 1]}, '_FloatValue1': {'type': 'f', 'desc': ['モザイクの粗さ'], 'presets': [0, 100, 200]}, '_Cutoff': {'type': 'f', 'desc': ['アルファのカットオフ値。', 'アルファ値がこの値より大きい部分だけがレンダリングされる'], 'presets': [0, 0.1, 0.5, 1, 5]}, '_ZTest': {'type': 'f', 'desc': ['デプステストの実行方法を指定する。'], 'disableSlider': True, 'preset_enums': [(0, 'Disabled:0'), (1, 'Never:1'), (2, 'Less:2'), (3, 'Equal:3'), (4, 'LessEqual:4'), (5, 'Greater:5'), (6, 'NotEqual:6'), (7, 'GreaterEqual:7'), (8, 'Always:8')]}, '_FloatValue2': {'type': 'f', 'presets': [-15, 0, 1, 15]}, '_FloatValue3': {'type': 'f', 'presets': [0, 0.1, 0.5, 1]}, '_ZTest2': {'type': 'f', 'disableSlider': True, 'preset_enums': [(0, '0'), (1, '1')]}, '_ZTest2Alpha': {'type': 'f', 'presets': [0, 0.8, 1]}}
class DataHandler:
227class DataHandler:
228    _instance = None
229
230    def __new__(cls):
231        if cls._instance is None:
232            cls._instance = super().__new__(cls)
233        return cls._instance
234
235    @classmethod
236    def instance(cls):
237        if cls._instance is None:
238            cls._instance = DataHandler()
239
240        return cls._instance
241
242    def __init__(self):
243        diffuse = {
244            'type_name': "リアル",
245            'icon': 'BRUSH_CLAY_STRIPS',
246            'shader2': 'Legacy Shaders__Diffuse',
247            'tex_list': ['_MainTex'],
248            'col_list': ['_Color'],
249            'f_list': [],
250        }
251        trans_diffuse = {
252            'type_name': "リアル 透過",
253            'icon': 'BRUSH_TEXFILL',
254            'shader2': 'Legacy Shaders__Transparent__Diffuse',
255            'tex_list': ['_MainTex'],
256            'col_list': ['_Color'],
257            'f_list': [],
258        }
259
260        self.shader_dict = {
261            'CM3D2/Toony_Lighted': {
262                'type_name': "トゥーン",
263                'icon': compat.icon('SHADING_SOLID'),
264                'shader2': 'CM3D2__Toony_Lighted',
265                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
266                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
267                'f_list': ['_Shininess', '_RimPower', '_RimShift']
268            },
269            'CM3D2/Toony_Lighted_Hair': {
270                'type_name': "トゥーン 髪",
271                'icon': 'PARTICLEMODE',
272                'shader2': 'CM3D2__Toony_Lighted_Hair',
273                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'],
274                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
275                'f_list': ['_Shininess', '_RimPower', '_RimShift', '_HiRate', '_HiPow']
276            },
277            'CM3D2/Toony_Lighted_Trans': {
278                'type_name': "トゥーン 透過",
279                'icon': compat.icon('SHADING_WIRE'),
280                'shader2': 'CM3D2__Toony_Lighted_Trans',
281                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
282                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
283                'f_list': ['_Shininess', '_Cutoff', '_RimPower', '_RimShift'],
284            },
285            'CM3D2/Toony_Lighted_Trans_NoZ': {
286                'type_name': "トゥーン 透過 NoZ",
287                'icon': 'DRIVER',
288                'shader2': 'CM3D2__Toony_Lighted_Trans_NoZ',
289                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
290                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
291                'f_list': ['_Shininess', '_RimPower', '_RimShift'],
292            },
293            'CM3D2/Toony_Lighted_Trans_NoZTest': {
294                'type_name': "トゥーン 透過 NoZTest",
295                'icon': 'ANIM_DATA',
296                'shader2': 'CM3D2__Toony_Lighted_Trans_NoZTest',
297                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
298                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
299                'f_list': ['_Shininess', '_RimPower', '_RimShift', '_ZTest', '_ZTest2', '_ZTest2Alpha'],
300            },
301            'CM3D2/Toony_Lighted_Outline': {
302                'type_name': "トゥーン 輪郭線",
303                'icon': 'ANTIALIASED',
304                'shader2': 'CM3D2__Toony_Lighted_Outline',
305                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
306                'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
307                'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'],
308            },
309            'CM3D2/Toony_Lighted_Outline_Tex': {
310                'type_name': "トゥーン 輪郭線 Tex",
311                'icon': 'MATSPHERE',
312                'shader2': 'CM3D2__Toony_Lighted_Outline_Tex',
313                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_OutlineTex', '_OutlineToonRamp'],
314                'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
315                'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'],
316            },
317            'CM3D2/Toony_Lighted_Hair_Outline': {
318                'type_name': "トゥーン 輪郭線 髪",
319                'icon': 'PARTICLEMODE',
320                'shader2': 'CM3D2__Toony_Lighted_Hair_Outline',
321                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'],
322                'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
323                'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift', '_HiRate', '_HiPow'],
324            },
325            # 'CM3D2/Toony_Lighted_Hair_Outline_Tex': {
326            # 	'type_name': "トゥーン 輪郭線 Tex 髪",
327            # 	'icon': 'PARTICLEMODE',
328            # 	'shader2': 'CM3D2__Toony_Lighted_Hair_Outline_Tex',
329            # 	'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'],
330            # 	'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
331            # 	'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift', '_HiRate', '_HiPow'],
332            # },
333            'CM3D2/Toony_Lighted_Outline_Trans': {
334                'type_name': "トゥーン 輪郭線 透過",
335                'icon': 'PROP_OFF',
336                'shader2': 'CM3D2__Toony_Lighted_Outline_Trans',
337                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
338                'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'],
339                'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'],
340            },
341            'CM3D2/Toony_Lighted_Cutout_AtC': {
342                'type_name': "トゥーン Cutout",
343                'icon': 'IPO_BACK',
344                'shader2': 'CM3D2__Toony_Lighted_Cutout_AtC',
345                'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'],
346                'col_list': ['_Color', '_ShadowColor', '_RimColor'],
347                'f_list': ['_Shininess', '_RimPower', '_RimShift', '_Cutoff'],
348            },
349            'CM3D2/Lighted_Trans': {
350                'type_name': "トゥーン無し 透過",
351                'icon': compat.icon('VIS_SEL_01'),
352                'shader2': 'CM3D2__Lighted_Trans',
353                'tex_list': ['_MainTex'],
354                'col_list': ['_Color', '_ShadowColor'],
355                'f_list': ['_Shininess'],
356            },
357            'CM3D2/Lighted': {
358                'type_name': "トゥーン無し",
359                'icon': compat.icon('VIS_SEL_11'),
360                'shader2': 'CM3D2__Lighted',
361                'tex_list': ['_MainTex'],
362                'col_list': ['_Color', '_ShadowColor'],
363                'f_list': ['_Shininess'],
364            },
365            'CM3D2/Lighted_Cutout_AtC': {
366                'type_name': "トゥーン無し Cutout",
367                'icon': 'IPO_BACK',
368                'shader2': 'CM3D2__Lighted_Cutout_AtC',
369                'tex_list': ['_MainTex'],
370                'col_list': ['_Color', '_ShadowColor'],
371                'f_list': ['_Shininess', '_Cutoff'],
372            },
373            'Unlit/Texture': {
374                'type_name': "発光",
375                'icon': 'PARTICLES',
376                'shader2': 'Unlit__Texture',
377                'tex_list': ['_MainTex'],
378                'col_list': [],  # ['_Color'],
379                'f_list': [],
380            },
381            'Unlit/Transparent': {
382                'type_name': "発光 透過",
383                'icon': 'MOD_PARTICLES',
384                'shader2': 'Unlit__Texture',
385                'tex_list': ['_MainTex'],
386                'col_list': [],  # ['_Color'],
387                'f_list': [],
388            },
389            'CM3D2/Mosaic': {
390                'type_name': "モザイク",
391                'icon': 'ALIASED',
392                'shader2': 'CM3D2__Mosaic',
393                'tex_list': ['_RenderTex'],
394                'col_list': [],
395                'f_list': ['_FloatValue1'],
396            },
397            'CM3D2/Man': {
398                'type_name': "ご主人様",
399                'icon': 'ARMATURE_DATA',
400                'shader2': 'CM3D2__Man',
401                'tex_list': [],
402                'col_list': ['_Color'],
403                'f_list': ['_FloatValue2', '_FloatValue3'],
404            },
405            'Diffuse': diffuse,
406            'Legacy Shaders/Diffuse': diffuse,
407            'Transparent/Diffuse': trans_diffuse,
408            'Legacy Shaders/Transparent/Diffuse': trans_diffuse,
409            'CM3D2_Debug/Debug_CM3D2_Normal2Color': {
410                'type_name': "法線",
411                'icon': compat.icon('NORMALS_VERTEX'),
412                'shader2': 'CM3D2_Debug__Debug_CM3D2_Normal2Color',
413                'tex_list': [],
414                'col_list': ['_Color'],  # , '_RimColor', '_OutlineColor', '_SpecColor'],
415                'f_list': []  # ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'],
416            },
417        }
418
419    @classmethod
420    def create_shader_items(cls) -> list:
421        _inst = cls.instance()
422        items = []
423        idx = 0
424        for name in SHADER_NAMES_CM3D2:
425            item = _inst.shader_dict.get(name)
426            if item:
427                items.append((name, item['type_name'], '', item['icon'], idx))
428                idx += 1
429        return items
430
431    @classmethod
432    def create_comshader_items(cls) -> list:
433        _inst = cls.instance()
434        items = []
435        idx = 0
436        for name in SHADER_NAMES_COM3D2:
437            item = _inst.shader_dict.get(name)
438            if item:
439                items.append((name, item['type_name'], '', item['icon'], idx))
440                idx += 1
441        return items
442
443    @classmethod
444    def get_shader_prop(cls, name):
445        _inst = cls.instance()
446        shader_prop = _inst.shader_dict.get(name)
447        if shader_prop:
448            return shader_prop
449
450        return {'type_name': '不明', 'icon': 'NONE'}
@classmethod
def instance(cls):
235    @classmethod
236    def instance(cls):
237        if cls._instance is None:
238            cls._instance = DataHandler()
239
240        return cls._instance
shader_dict
@classmethod
def create_shader_items(cls) -> list:
419    @classmethod
420    def create_shader_items(cls) -> list:
421        _inst = cls.instance()
422        items = []
423        idx = 0
424        for name in SHADER_NAMES_CM3D2:
425            item = _inst.shader_dict.get(name)
426            if item:
427                items.append((name, item['type_name'], '', item['icon'], idx))
428                idx += 1
429        return items
@classmethod
def create_comshader_items(cls) -> list:
431    @classmethod
432    def create_comshader_items(cls) -> list:
433        _inst = cls.instance()
434        items = []
435        idx = 0
436        for name in SHADER_NAMES_COM3D2:
437            item = _inst.shader_dict.get(name)
438            if item:
439                items.append((name, item['type_name'], '', item['icon'], idx))
440                idx += 1
441        return items
@classmethod
def get_shader_prop(cls, name):
443    @classmethod
444    def get_shader_prop(cls, name):
445        _inst = cls.instance()
446        shader_prop = _inst.shader_dict.get(name)
447        if shader_prop:
448            return shader_prop
449
450        return {'type_name': '不明', 'icon': 'NONE'}
Handler = <CM3D2 Converter.cm3d2_data.DataHandler object>
class Material:
455class Material():
456    """マテリアルデータクラス"""
457    def __init__(self):
458        self.version = 1000
459        self.name1 = None
460        self.name2 = None
461        self.shader1 = None
462        self.shader2 = None
463
464        self.tex_list = []  # prop_name, (tex_name, tex_path, trans[2], scale[2])
465        self.col_list = []  # prop_name, col[4]
466        self.f_list = []  # prop_name, f
467        self.range_list = [] # prop_name, col[4]
468
469        self.custom_list = dict()
470
471    def sort(self):
472        self.tex_list = sorted(self.tex_list, key=lambda item: item[0])
473        self.col_list = sorted(self.col_list, key=lambda item: item[0])
474        self.f_list = sorted(self.f_list, key=lambda item: item[0])
475
476    @property
477    def name(self):
478        return self.name2 or self.name1
479
480    def read(self, reader, read_header=True):
481        if read_header:
482            header = common.read_str(reader)
483            if header != 'CM3D2_MATERIAL':
484                raise common.CM3D2ImportException(f_tip_("mateファイルではありません。ヘッダ:{}", header))
485            self.version = struct.unpack('<i', reader.read(4))[0]
486            self.name1 = common.read_str(reader)
487        self.name2 = common.read_str(reader)
488
489        self.shader1 = common.read_str(reader)
490        self.shader2 = common.read_str(reader)
491        
492        peeked = reader.peek()[0]
493        print(f"type({peeked}) = {type(peeked)}")
494        if self.version >= 2102 and (peeked in (0, 1)): # CR Edit Mode
495            cr_unknown_float_count = struct.unpack('<B', reader.read(1))[0]
496            for i in range(cr_unknown_float_count):
497                # CR TODO
498                self.custom_list[f'cr_unknown_float:{i:03d}'] = struct.unpack('<f', reader.read(4))
499
500        for i in range(99999):
501            prop_type = common.read_str(reader)
502            if prop_type == 'tex':
503                prop_name = common.read_str(reader)
504                sub_type = common.read_str(reader)
505                if sub_type == 'tex2d':
506                    tex_name = common.read_str(reader)
507                    tex_path = common.read_str(reader)
508                    offset = struct.unpack('<2f', reader.read(4 * 2))
509                    scale = struct.unpack('<2f', reader.read(4 * 2))
510                    tex_item = [prop_name, tex_name, tex_path, offset, scale]
511                else:
512                    tex_item = [prop_name]
513                self.tex_list.append(tex_item)
514
515            elif prop_type == 'col':
516                prop_name = common.read_str(reader)
517                col = struct.unpack('<4f', reader.read(4 * 4))
518                self.col_list.append([prop_name, col])
519
520            elif prop_type == 'f' or prop_type == 'range': # 'range' from CR Edit
521                prop_name = common.read_str(reader)
522                f = struct.unpack('<f', reader.read(4))[0]
523                self.f_list.append([prop_name, f])
524            
525            # CR TODO
526            elif prop_type == 'keyword':
527                prop_name = common.read_str(reader)
528                keyword_f = struct.unpack('<f', reader.read(4))[0]
529                print(keyword_f, type(keyword_f))
530                self.custom_list.setdefault('keyword', dict())[prop_name] = keyword_f #.append([prop_name, keyword_f])
531                
532            # CR TODO
533            elif prop_type == '_ALPHAPREMULTIPLY_ON':
534                alpha_bool = struct.unpack('<?', reader.read(1))[0]
535                self.custom_list['_ALPHAPREMULTIPLY_ON'] = alpha_bool
536
537            elif prop_type == 'end':
538                break
539            else:
540                raise common.CM3D2ImportException(f_tip_("Materialプロパティに未知の設定値タイプ({prop})が見つかりました。", prop=prop_type))
541
542    def write(self, writer, write_header=True):
543        if write_header:
544            common.write_str(writer, 'CM3D2_MATERIAL')
545            writer.write(struct.pack('<i', self.version))
546            common.write_str(writer, self.name1)
547
548        common.write_str(writer, self.name2)
549        common.write_str(writer, self.shader1)
550        common.write_str(writer, self.shader2)
551
552        for tex_item in self.tex_list:
553            common.write_str(writer, 'tex')
554            common.write_str(writer, tex_item[0])  # prop_name
555
556            if len(tex_item) < 2:
557                common.write_str(writer, 'null')
558            else:
559                common.write_str(writer, 'tex2d')
560                common.write_str(writer, tex_item[1])  # tex_name
561                common.write_str(writer, tex_item[2])  # tex_path
562                trans = tex_item[3]
563                writer.write(struct.pack('<2f', trans[0], trans[1]))
564                scale = tex_item[4]
565                writer.write(struct.pack('<2f', scale[0], scale[1]))
566
567        for col_item in self.col_list:
568            common.write_str(writer, 'col')
569            common.write_str(writer, col_item[0])  # prop_name
570
571            col = col_item[1]
572            writer.write(struct.pack('<4f', col[0], col[1], col[2], col[3]))
573
574        for f_item in self.f_list:
575            common.write_str(writer, 'f')
576            common.write_str(writer, f_item[0])  # prop_name
577
578            writer.write(struct.pack('<f', f_item[1]))
579
580        common.write_str(writer, 'end')
581
582    def to_text(self):
583        output_text = str(self.version) + "\n"
584        output_text += self.name1 + "\n"
585        output_text += self.name2 + "\n"
586        output_text += self.shader1 + "\n"
587        output_text += self.shader2 + "\n"
588        output_text += "\n"
589
590        for tex_item in self.tex_list:
591            output_text += 'tex\n'
592            output_text += "\t" + tex_item[0] + "\n"  # prop_name
593
594            if len(tex_item) < 2:
595                output_text += '\tnull\n'
596            else:
597                output_text += '\ttex2d\n'
598                output_text += "\t" + tex_item[1] + "\n"  # tex_name
599                output_text += "\t" + tex_item[2] + "\n"  # tex_path
600                trans = tex_item[3]
601                scale = tex_item[4]
602                output_text += "\t" + " ".join([str(trans[0]), str(trans[1]), str(scale[0]), str(scale[1])]) + "\n"
603
604        for col_item in self.col_list:
605            output_text += 'col\n'
606            output_text += "\t" + col_item[0] + "\n"  # prop_name
607            col = col_item[1]
608            output_text += "\t" + " ".join([str(col[0]), str(col[1]), str(col[2]), str(col[3])]) + "\n"  # prop_name
609
610        for f_item in self.f_list:
611            output_text += 'f\n'
612            output_text += "\t" + f_item[0] + "\n"  # prop_name
613            f = f_item[1]
614            output_text += "\t" + str(f) + "\n"
615
616        return output_text
617
618    def to_json(self):
619        import json
620        return json.dumps(self.__dict__, ensure_ascii=False, indent=2)
621
622    def from_dict(self, data):
623        self.name1 = data['name1']
624        self.name2 = data['name2']
625        self.version = data['version']
626        self.shader1 = data['shader1']
627        self.shader2 = data['shader2']
628
629        self.tex_list = data['tex_list']  # prop_name, (tex_name, tex_path, trans[2], scale[2])
630        self.col_list = data['col_list']  # prop_name, col[4]
631        self.f_list = data['f_list']  # prop_name, f

マテリアルデータクラス

version
name1
name2
shader1
shader2
tex_list
col_list
f_list
range_list
custom_list
def sort(self):
471    def sort(self):
472        self.tex_list = sorted(self.tex_list, key=lambda item: item[0])
473        self.col_list = sorted(self.col_list, key=lambda item: item[0])
474        self.f_list = sorted(self.f_list, key=lambda item: item[0])
name
def read(self, reader, read_header=True):
480    def read(self, reader, read_header=True):
481        if read_header:
482            header = common.read_str(reader)
483            if header != 'CM3D2_MATERIAL':
484                raise common.CM3D2ImportException(f_tip_("mateファイルではありません。ヘッダ:{}", header))
485            self.version = struct.unpack('<i', reader.read(4))[0]
486            self.name1 = common.read_str(reader)
487        self.name2 = common.read_str(reader)
488
489        self.shader1 = common.read_str(reader)
490        self.shader2 = common.read_str(reader)
491        
492        peeked = reader.peek()[0]
493        print(f"type({peeked}) = {type(peeked)}")
494        if self.version >= 2102 and (peeked in (0, 1)): # CR Edit Mode
495            cr_unknown_float_count = struct.unpack('<B', reader.read(1))[0]
496            for i in range(cr_unknown_float_count):
497                # CR TODO
498                self.custom_list[f'cr_unknown_float:{i:03d}'] = struct.unpack('<f', reader.read(4))
499
500        for i in range(99999):
501            prop_type = common.read_str(reader)
502            if prop_type == 'tex':
503                prop_name = common.read_str(reader)
504                sub_type = common.read_str(reader)
505                if sub_type == 'tex2d':
506                    tex_name = common.read_str(reader)
507                    tex_path = common.read_str(reader)
508                    offset = struct.unpack('<2f', reader.read(4 * 2))
509                    scale = struct.unpack('<2f', reader.read(4 * 2))
510                    tex_item = [prop_name, tex_name, tex_path, offset, scale]
511                else:
512                    tex_item = [prop_name]
513                self.tex_list.append(tex_item)
514
515            elif prop_type == 'col':
516                prop_name = common.read_str(reader)
517                col = struct.unpack('<4f', reader.read(4 * 4))
518                self.col_list.append([prop_name, col])
519
520            elif prop_type == 'f' or prop_type == 'range': # 'range' from CR Edit
521                prop_name = common.read_str(reader)
522                f = struct.unpack('<f', reader.read(4))[0]
523                self.f_list.append([prop_name, f])
524            
525            # CR TODO
526            elif prop_type == 'keyword':
527                prop_name = common.read_str(reader)
528                keyword_f = struct.unpack('<f', reader.read(4))[0]
529                print(keyword_f, type(keyword_f))
530                self.custom_list.setdefault('keyword', dict())[prop_name] = keyword_f #.append([prop_name, keyword_f])
531                
532            # CR TODO
533            elif prop_type == '_ALPHAPREMULTIPLY_ON':
534                alpha_bool = struct.unpack('<?', reader.read(1))[0]
535                self.custom_list['_ALPHAPREMULTIPLY_ON'] = alpha_bool
536
537            elif prop_type == 'end':
538                break
539            else:
540                raise common.CM3D2ImportException(f_tip_("Materialプロパティに未知の設定値タイプ({prop})が見つかりました。", prop=prop_type))
def write(self, writer, write_header=True):
542    def write(self, writer, write_header=True):
543        if write_header:
544            common.write_str(writer, 'CM3D2_MATERIAL')
545            writer.write(struct.pack('<i', self.version))
546            common.write_str(writer, self.name1)
547
548        common.write_str(writer, self.name2)
549        common.write_str(writer, self.shader1)
550        common.write_str(writer, self.shader2)
551
552        for tex_item in self.tex_list:
553            common.write_str(writer, 'tex')
554            common.write_str(writer, tex_item[0])  # prop_name
555
556            if len(tex_item) < 2:
557                common.write_str(writer, 'null')
558            else:
559                common.write_str(writer, 'tex2d')
560                common.write_str(writer, tex_item[1])  # tex_name
561                common.write_str(writer, tex_item[2])  # tex_path
562                trans = tex_item[3]
563                writer.write(struct.pack('<2f', trans[0], trans[1]))
564                scale = tex_item[4]
565                writer.write(struct.pack('<2f', scale[0], scale[1]))
566
567        for col_item in self.col_list:
568            common.write_str(writer, 'col')
569            common.write_str(writer, col_item[0])  # prop_name
570
571            col = col_item[1]
572            writer.write(struct.pack('<4f', col[0], col[1], col[2], col[3]))
573
574        for f_item in self.f_list:
575            common.write_str(writer, 'f')
576            common.write_str(writer, f_item[0])  # prop_name
577
578            writer.write(struct.pack('<f', f_item[1]))
579
580        common.write_str(writer, 'end')
def to_text(self):
582    def to_text(self):
583        output_text = str(self.version) + "\n"
584        output_text += self.name1 + "\n"
585        output_text += self.name2 + "\n"
586        output_text += self.shader1 + "\n"
587        output_text += self.shader2 + "\n"
588        output_text += "\n"
589
590        for tex_item in self.tex_list:
591            output_text += 'tex\n'
592            output_text += "\t" + tex_item[0] + "\n"  # prop_name
593
594            if len(tex_item) < 2:
595                output_text += '\tnull\n'
596            else:
597                output_text += '\ttex2d\n'
598                output_text += "\t" + tex_item[1] + "\n"  # tex_name
599                output_text += "\t" + tex_item[2] + "\n"  # tex_path
600                trans = tex_item[3]
601                scale = tex_item[4]
602                output_text += "\t" + " ".join([str(trans[0]), str(trans[1]), str(scale[0]), str(scale[1])]) + "\n"
603
604        for col_item in self.col_list:
605            output_text += 'col\n'
606            output_text += "\t" + col_item[0] + "\n"  # prop_name
607            col = col_item[1]
608            output_text += "\t" + " ".join([str(col[0]), str(col[1]), str(col[2]), str(col[3])]) + "\n"  # prop_name
609
610        for f_item in self.f_list:
611            output_text += 'f\n'
612            output_text += "\t" + f_item[0] + "\n"  # prop_name
613            f = f_item[1]
614            output_text += "\t" + str(f) + "\n"
615
616        return output_text
def to_json(self):
618    def to_json(self):
619        import json
620        return json.dumps(self.__dict__, ensure_ascii=False, indent=2)
def from_dict(self, data):
622    def from_dict(self, data):
623        self.name1 = data['name1']
624        self.name2 = data['name2']
625        self.version = data['version']
626        self.shader1 = data['shader1']
627        self.shader2 = data['shader2']
628
629        self.tex_list = data['tex_list']  # prop_name, (tex_name, tex_path, trans[2], scale[2])
630        self.col_list = data['col_list']  # prop_name, col[4]
631        self.f_list = data['f_list']  # prop_name, f
class MaterialHandler:
 634class MaterialHandler:
 635
 636    @classmethod
 637    def parse_tex_node(cls, node, remove_serial=True):
 638        node_name = common.remove_serial_number(node.name, remove_serial)
 639        tex_item = [node_name]
 640
 641        try:
 642            img = node.image
 643        except:
 644            raise common.CM3D2ImportException('Materialプロパティのtexタイプの設定値取得に失敗しました。')
 645
 646        if img:
 647            tex_name = common.remove_serial_number(img.name, remove_serial)
 648            tex_name = common.re_png.sub(r'\1', tex_name)  # 拡張子を除外
 649            tex_item.append(tex_name)
 650
 651            if 'cm3d2_path' in img:
 652                path = img['cm3d2_path']
 653            else:
 654                path = bpy.path.abspath(img.filepath)
 655            path = common.to_cm3d2path(path)
 656
 657            tex_item.append(path)
 658            tex_map = node.texture_mapping
 659            tex_trans = tex_map.translation[:2]
 660            tex_scale = tex_map.scale[:2]
 661            tex_item.append(tex_trans)
 662            tex_item.append(tex_scale)
 663        return tex_item
 664
 665    @classmethod
 666    def parse_col_node(cls, node, remove_serial=True):
 667        node_name = common.remove_serial_number(node.name, remove_serial)
 668        col = node.outputs[0].default_value
 669        return [node_name, col[:4]]
 670
 671    @classmethod
 672    def parse_f_node(cls, node, remove_serial=True):
 673        node_name = common.remove_serial_number(node.name, remove_serial)
 674        f = node.outputs[0].default_value
 675        return [node_name, f]
 676
 677    @classmethod
 678    def read(cls, reader, read_header=True, version=None):
 679        if not read_header and version == None:
 680            raise ValueError(f_tip_("The argument 'version' is required when 'read_header' is False for MaterialHandler.read()"))
 681        mat_data = Material()
 682        if version:
 683            mat_data.version = version
 684        mat_data.read(reader, read_header)
 685
 686        return mat_data
 687
 688    @classmethod
 689    def get_shader_prop_dynamic(cls, mate, ):
 690        lists = { 'VALUE':'f_list', 'RGB':'col_list', 'TEX_IMAGE':'tex_list' }
 691        shader_prop = copy.deepcopy( DataHandler.get_shader_prop(mate.get('shader1')) )
 692        for node in mate.node_tree.nodes:
 693            if node.name[0] != '_':
 694                continue
 695            list_name = lists.get(node.type)
 696            if not list_name:
 697                continue
 698            prop_list = shader_prop.get(list_name)
 699            if not prop_list:
 700                prop_list = list()
 701                shader_prop[list_name] = prop_list
 702            if node.name in prop_list:
 703                continue
 704            prop_list.append(node.name)
 705        return shader_prop
 706
 707    @classmethod
 708    def parse_mate(cls, mate, remove_serial=True):
 709        mat_data = Material()
 710
 711        mate_name = common.remove_serial_number(mate.name, remove_serial)
 712        mat_data.name1 = mate_name.lower()
 713        mat_data.name2 = mate_name
 714        mat_data.shader1 = mate['shader1']
 715        mat_data.shader2 = mate['shader2']
 716
 717        nodes = mate.node_tree.nodes
 718        shader_prop = cls.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(mat_data.shader1)
 719        if shader_prop:
 720            for node_name in shader_prop['tex_list']:
 721                node = nodes.get(node_name)
 722                if node and node.type == 'TEX_IMAGE':
 723                    tex_item = cls.parse_tex_node(node, remove_serial)
 724                    mat_data.tex_list.append(tex_item)
 725
 726            for node_name in shader_prop['col_list']:
 727                node = nodes.get(node_name)
 728                if node and node.type == 'RGB':
 729                    col_item = cls.parse_col_node(node, remove_serial)
 730                    mat_data.col_list.append(col_item)
 731
 732            for node_name in shader_prop['f_list']:
 733                node = nodes.get(node_name)
 734                if node and node.type == 'VALUE':
 735                    f_item = cls.parse_f_node(node, remove_serial)
 736                    mat_data.f_list.append(f_item)
 737
 738        #for node in nodes:
 739        #    if not node.name.startswith('_'):
 740        #        continue
 741        #
 742        #    node_type = node.type
 743        #    if node_type == 'TEX_IMAGE':
 744        #        tex_item = cls.parse_tex_node(node, remove_serial)
 745        #        mat_data.tex_list.append(tex_item)
 746        #    elif node_type == 'RGB':
 747        #        col_item = cls.parse_col_node(node, remove_serial)
 748        #        mat_data.col_list.append(col_item)
 749        #
 750        #    elif node_type == 'VALUE':
 751        #        f_item = cls.parse_f_node(node, remove_serial)
 752        #        mat_data.f_list.append(f_item)
 753        
 754        return mat_data
 755
 756    @classmethod
 757    def parse_mate_old(cls, mate, remove_serial=True):
 758        """material parser for blender-2.7x"""
 759        mat_data = Material()
 760
 761        mate_name = common.remove_serial_number(mate.name, remove_serial)
 762        mat_data.name1 = mate_name.lower()
 763        mat_data.name2 = mate_name
 764        mat_data.shader1 = mate['shader1']
 765        mat_data.shader2 = mate['shader2']
 766
 767        for tindex, tslot in enumerate(mate.texture_slots):
 768            if not tslot:
 769                continue
 770
 771            tex = tslot.texture
 772            node_name = common.remove_serial_number(tex.name, remove_serial)
 773            # node_name = tslot.name
 774            if mate.use_textures[tindex]:
 775                tex_item = [node_name]
 776                try:
 777                    img = tex.image
 778                except:
 779                    raise Exception('Materialプロパティのtexタイプの設定値取得に失敗しました。')
 780
 781                if img:
 782                    tex_name = common.remove_serial_number(img.name, remove_serial)
 783                    tex_name = common.re_png.sub(r'\1', tex_name)  # 拡張子を除外
 784                    tex_item.append(tex_name)
 785
 786                    if 'cm3d2_path' in img:
 787                        path = img['cm3d2_path']
 788                    else:
 789                        path = bpy.path.abspath(img.filepath)
 790                    path = common.to_cm3d2path(path)
 791
 792                    tex_item.append(path)
 793                    tex_trans = tex.offset[:2]
 794                    tex_scale = tex.scale[:2]
 795                    tex_item.append(tex_trans)
 796                    tex_item.append(tex_scale)
 797
 798                mat_data.tex_list.append(tex_item)
 799            elif tslot.use_rgb_to_intensity:
 800                col = tex.color[:3] + [tslot.diffuse_color_factor]
 801                mat_data.col_list.append([node_name, col[:4]])
 802            else:
 803                f = tslot.diffuse_color_factor
 804                mat_data.f_list.append([node_name, f])
 805
 806        return mat_data
 807
 808    @classmethod
 809    def parse_text(cls, text):
 810        mat_data = Material()
 811        lines = text.split('\n')
 812
 813        mat_data.version = int(lines[0])
 814        mat_data.name1 = lines[1]
 815        mat_data.name2 = lines[2]
 816        mat_data.shader1 = lines[3]
 817        mat_data.shader2 = lines[4]
 818
 819        line_seek = 5
 820        while line_seek < len(lines):
 821            node_type = common.line_trim(lines[line_seek])
 822            if not node_type:
 823                line_seek += 1
 824                continue
 825            if node_type == 'tex':
 826                prop_name = common.line_trim(lines[line_seek + 1])
 827                sub_type = common.line_trim(lines[line_seek + 2])
 828                if sub_type == 'tex2d':
 829                    line_seek += 3
 830                    tex_name = common.line_trim(lines[line_seek])
 831                    tex_path = common.line_trim(lines[line_seek + 1])
 832                    tex_map = common.line_trim(lines[line_seek + 2]).split(' ')
 833                    for map_datum in range(len(tex_map)):
 834                        tex_map[map_datum] = float(tex_map[map_datum])
 835                    mat_data.tex_list.append([prop_name, tex_name, tex_path, tex_map[:2], tex_map[2:]])
 836                else:
 837                    mat_data.tex_list.append([prop_name])
 838
 839                line_seek += 3
 840
 841            elif node_type == 'col':
 842                prop_name = common.line_trim(lines[line_seek + 1])
 843                tex_map = common.line_trim(lines[line_seek + 2]).split(' ')
 844                for map_datum in range(len(tex_map)):
 845                    tex_map[map_datum] = float(tex_map[map_datum])
 846
 847                mat_data.col_list.append([prop_name, tex_map[:]])
 848                line_seek += 3
 849
 850            elif node_type == 'f':
 851                prop_name = common.line_trim(lines[line_seek + 1])
 852                val = float(common.line_trim(lines[line_seek + 2]))
 853
 854                mat_data.f_list.append([prop_name, val])
 855                line_seek += 3
 856            else:
 857                raise Exception('未知の設定値タイプが見つかりました。')
 858
 859        return mat_data
 860
 861    @classmethod
 862    def parse_json(cls, text):
 863        import json
 864        mat_data = Material()
 865        mat_data.from_dict(json.loads(text))
 866
 867        return mat_data
 868
 869    @classmethod
 870    def apply_to(cls, override, mate, mat_data, replace_tex=True):
 871        mate['shader1'] = mat_data.shader1
 872        mate['shader2'] = mat_data.shader2
 873
 874        if mate.use_nodes is False:
 875            mate.use_nodes = True
 876
 877        nodes = mate.node_tree.nodes
 878        nodes.clear()
 879        # OUTPUT_MATERIAL, BSDF_PRINCIPLEDは消さない
 880        #if len(nodes) > 2:
 881        #    clear_nodes(nodes)
 882
 883        for tex_item in mat_data.tex_list:
 884            prop_name = tex_item[0]
 885
 886            if len(tex_item) < 2:
 887                common.create_tex(override, mate, prop_name)
 888            else:
 889                tex_name = tex_item[1]
 890                tex_path = tex_item[2]
 891                tex_map = tex_item[3] + tex_item[4]
 892                common.create_tex(override, mate, prop_name, tex_name, tex_path, tex_path, tex_map, replace_tex)
 893
 894        for col_item in mat_data.col_list:
 895            prop_name = col_item[0]
 896            col = col_item[1]
 897            common.create_col(override, mate, prop_name, col)
 898
 899        for item in mat_data.f_list:
 900            prop_name = item[0]
 901            f = item[1]
 902            common.create_float(override, mate, prop_name, f)
 903
 904        for key, value in mat_data.custom_list.items():
 905            mate[key] = value
 906            if type(value) == bool:
 907                rna_ui = mate.get('_RNA_UI', dict())
 908                rna_ui[key] = {
 909                    "default"  : value,
 910                    "min"      : 0    ,
 911                    "max"      : 1    ,
 912                    "soft_min" : 0    ,
 913                    "soft_max" : 1    ,
 914                }
 915                mate['_RNA_UI'] = rna_ui
 916
 917
 918
 919        align_nodes(mate)
 920
 921    @classmethod
 922    def apply_to_old(cls, override, mate, mat_data, replace_tex=True, decorate=True, skip_same_prop=True):
 923        ob = override['active_object']
 924        me = ob.data
 925
 926        mate['shader1'] = mat_data.shader1
 927        mate['shader2'] = mat_data.shader2
 928
 929        slot_index = 0
 930        olds_slots = {}
 931        read_texes = set() if skip_same_prop else None
 932
 933        for item in mat_data.tex_list:
 934            prop_name = item[0]
 935            if read_texes:
 936                if prop_name in read_texes:
 937                    continue
 938                read_texes.add(prop_name)
 939
 940            slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'IMAGE')
 941            slot.use_rgb_to_intensity = False
 942            mate.use_textures[slot_index] = True
 943
 944            if len(item) > 4:
 945                tex = slot.texture
 946                tex_name = item[1]
 947                tex_path = item[2]
 948                if tex_name in read_texes:
 949                    continue
 950
 951                if tex_name != common.remove_serial_number(tex.image.name):
 952                    tex.image.name = tex_name
 953                tex.image['cm3d2_path'] = tex_path
 954                tex.image.filepath = tex.image['cm3d2_path']
 955
 956                slot.offset = item[3]
 957                slot.scale = item[4]
 958                if replace_tex:
 959                    if common.replace_cm3d2_tex(tex.image, reload_path=True) and prop_name == '_MainTex':
 960                        for face in me.polygons:
 961                            if face.material_index == ob.active_material_index:
 962                                me.uv_textures.active.data[face.index].image = tex.image
 963            slot_index += 1
 964
 965        for item in mat_data.col_list:
 966            prop_name = item[0]
 967            col = item[1]
 968
 969            slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND')
 970
 971            mate.use_textures[slot_index] = False
 972            slot.use_rgb_to_intensity = True
 973            slot.color = col[:3]
 974            slot.diffuse_color_factor = col[3]
 975
 976            slot_index += 1
 977
 978        for item in mat_data.f_list:
 979            prop_name = item[0]
 980            f = item[1]
 981
 982            slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND')
 983
 984            mate.use_textures[slot_index] = False
 985            slot.use_rgb_to_intensity = False
 986            slot.diffuse_color_factor = f
 987
 988            slot_index += 1
 989
 990        # 存在しないスロットをクリア
 991        for item_index in range(slot_index, len(mate.texture_slots)):
 992            if mate.texture_slots[item_index]:
 993                mate.texture_slots.clear(item_index)
 994
 995        # プレビューへの反映
 996        for slot in mate.texture_slots:
 997            if slot:
 998                common.set_texture_color(slot)
 999
1000        if decorate:
1001            common.decorate_material(mate, decorate, me, ob.active_material_index)
1002
1003    @staticmethod
1004    def search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, tex_type):
1005        tex = None
1006        slot_item = mate.texture_slots[slot_index]
1007        slot_name = slot_item.name if slot_item else ''
1008
1009        slot_name = common.remove_serial_number(slot_name)
1010        # 指定スロットが同名であればそのスロットをそのまま利用する
1011        if prop_name == slot_name:
1012            slot = slot_item
1013        else:
1014            # スロット名が異なり、既にスロットがある場合はキャッシュに格納
1015            if slot_item:
1016                olds_slots[slot_name] = slot_item
1017            slot = mate.texture_slots.create(slot_index)
1018
1019            if prop_name in olds_slots:
1020                tex = olds_slots.pop(prop_name).texture
1021            else:
1022                for item_index in range(slot_index + 1, len(mate.texture_slots)):
1023                    slot_item = mate.texture_slots[item_index]
1024                    if slot_item is None:
1025                        break
1026                    if prop_name == common.remove_serial_number(slot_item.name):
1027                        tex = slot_item.texture
1028                        break
1029            if tex is None:
1030                tex = override['blend_data'].textures.new(prop_name, tex_type)
1031            slot.texture = tex
1032        return slot
@classmethod
def parse_tex_node(cls, node, remove_serial=True):
636    @classmethod
637    def parse_tex_node(cls, node, remove_serial=True):
638        node_name = common.remove_serial_number(node.name, remove_serial)
639        tex_item = [node_name]
640
641        try:
642            img = node.image
643        except:
644            raise common.CM3D2ImportException('Materialプロパティのtexタイプの設定値取得に失敗しました。')
645
646        if img:
647            tex_name = common.remove_serial_number(img.name, remove_serial)
648            tex_name = common.re_png.sub(r'\1', tex_name)  # 拡張子を除外
649            tex_item.append(tex_name)
650
651            if 'cm3d2_path' in img:
652                path = img['cm3d2_path']
653            else:
654                path = bpy.path.abspath(img.filepath)
655            path = common.to_cm3d2path(path)
656
657            tex_item.append(path)
658            tex_map = node.texture_mapping
659            tex_trans = tex_map.translation[:2]
660            tex_scale = tex_map.scale[:2]
661            tex_item.append(tex_trans)
662            tex_item.append(tex_scale)
663        return tex_item
@classmethod
def parse_col_node(cls, node, remove_serial=True):
665    @classmethod
666    def parse_col_node(cls, node, remove_serial=True):
667        node_name = common.remove_serial_number(node.name, remove_serial)
668        col = node.outputs[0].default_value
669        return [node_name, col[:4]]
@classmethod
def parse_f_node(cls, node, remove_serial=True):
671    @classmethod
672    def parse_f_node(cls, node, remove_serial=True):
673        node_name = common.remove_serial_number(node.name, remove_serial)
674        f = node.outputs[0].default_value
675        return [node_name, f]
@classmethod
def read(cls, reader, read_header=True, version=None):
677    @classmethod
678    def read(cls, reader, read_header=True, version=None):
679        if not read_header and version == None:
680            raise ValueError(f_tip_("The argument 'version' is required when 'read_header' is False for MaterialHandler.read()"))
681        mat_data = Material()
682        if version:
683            mat_data.version = version
684        mat_data.read(reader, read_header)
685
686        return mat_data
@classmethod
def get_shader_prop_dynamic(cls, mate):
688    @classmethod
689    def get_shader_prop_dynamic(cls, mate, ):
690        lists = { 'VALUE':'f_list', 'RGB':'col_list', 'TEX_IMAGE':'tex_list' }
691        shader_prop = copy.deepcopy( DataHandler.get_shader_prop(mate.get('shader1')) )
692        for node in mate.node_tree.nodes:
693            if node.name[0] != '_':
694                continue
695            list_name = lists.get(node.type)
696            if not list_name:
697                continue
698            prop_list = shader_prop.get(list_name)
699            if not prop_list:
700                prop_list = list()
701                shader_prop[list_name] = prop_list
702            if node.name in prop_list:
703                continue
704            prop_list.append(node.name)
705        return shader_prop
@classmethod
def parse_mate(cls, mate, remove_serial=True):
707    @classmethod
708    def parse_mate(cls, mate, remove_serial=True):
709        mat_data = Material()
710
711        mate_name = common.remove_serial_number(mate.name, remove_serial)
712        mat_data.name1 = mate_name.lower()
713        mat_data.name2 = mate_name
714        mat_data.shader1 = mate['shader1']
715        mat_data.shader2 = mate['shader2']
716
717        nodes = mate.node_tree.nodes
718        shader_prop = cls.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(mat_data.shader1)
719        if shader_prop:
720            for node_name in shader_prop['tex_list']:
721                node = nodes.get(node_name)
722                if node and node.type == 'TEX_IMAGE':
723                    tex_item = cls.parse_tex_node(node, remove_serial)
724                    mat_data.tex_list.append(tex_item)
725
726            for node_name in shader_prop['col_list']:
727                node = nodes.get(node_name)
728                if node and node.type == 'RGB':
729                    col_item = cls.parse_col_node(node, remove_serial)
730                    mat_data.col_list.append(col_item)
731
732            for node_name in shader_prop['f_list']:
733                node = nodes.get(node_name)
734                if node and node.type == 'VALUE':
735                    f_item = cls.parse_f_node(node, remove_serial)
736                    mat_data.f_list.append(f_item)
737
738        #for node in nodes:
739        #    if not node.name.startswith('_'):
740        #        continue
741        #
742        #    node_type = node.type
743        #    if node_type == 'TEX_IMAGE':
744        #        tex_item = cls.parse_tex_node(node, remove_serial)
745        #        mat_data.tex_list.append(tex_item)
746        #    elif node_type == 'RGB':
747        #        col_item = cls.parse_col_node(node, remove_serial)
748        #        mat_data.col_list.append(col_item)
749        #
750        #    elif node_type == 'VALUE':
751        #        f_item = cls.parse_f_node(node, remove_serial)
752        #        mat_data.f_list.append(f_item)
753        
754        return mat_data
@classmethod
def parse_mate_old(cls, mate, remove_serial=True):
756    @classmethod
757    def parse_mate_old(cls, mate, remove_serial=True):
758        """material parser for blender-2.7x"""
759        mat_data = Material()
760
761        mate_name = common.remove_serial_number(mate.name, remove_serial)
762        mat_data.name1 = mate_name.lower()
763        mat_data.name2 = mate_name
764        mat_data.shader1 = mate['shader1']
765        mat_data.shader2 = mate['shader2']
766
767        for tindex, tslot in enumerate(mate.texture_slots):
768            if not tslot:
769                continue
770
771            tex = tslot.texture
772            node_name = common.remove_serial_number(tex.name, remove_serial)
773            # node_name = tslot.name
774            if mate.use_textures[tindex]:
775                tex_item = [node_name]
776                try:
777                    img = tex.image
778                except:
779                    raise Exception('Materialプロパティのtexタイプの設定値取得に失敗しました。')
780
781                if img:
782                    tex_name = common.remove_serial_number(img.name, remove_serial)
783                    tex_name = common.re_png.sub(r'\1', tex_name)  # 拡張子を除外
784                    tex_item.append(tex_name)
785
786                    if 'cm3d2_path' in img:
787                        path = img['cm3d2_path']
788                    else:
789                        path = bpy.path.abspath(img.filepath)
790                    path = common.to_cm3d2path(path)
791
792                    tex_item.append(path)
793                    tex_trans = tex.offset[:2]
794                    tex_scale = tex.scale[:2]
795                    tex_item.append(tex_trans)
796                    tex_item.append(tex_scale)
797
798                mat_data.tex_list.append(tex_item)
799            elif tslot.use_rgb_to_intensity:
800                col = tex.color[:3] + [tslot.diffuse_color_factor]
801                mat_data.col_list.append([node_name, col[:4]])
802            else:
803                f = tslot.diffuse_color_factor
804                mat_data.f_list.append([node_name, f])
805
806        return mat_data

material parser for blender-2.7x

@classmethod
def parse_text(cls, text):
808    @classmethod
809    def parse_text(cls, text):
810        mat_data = Material()
811        lines = text.split('\n')
812
813        mat_data.version = int(lines[0])
814        mat_data.name1 = lines[1]
815        mat_data.name2 = lines[2]
816        mat_data.shader1 = lines[3]
817        mat_data.shader2 = lines[4]
818
819        line_seek = 5
820        while line_seek < len(lines):
821            node_type = common.line_trim(lines[line_seek])
822            if not node_type:
823                line_seek += 1
824                continue
825            if node_type == 'tex':
826                prop_name = common.line_trim(lines[line_seek + 1])
827                sub_type = common.line_trim(lines[line_seek + 2])
828                if sub_type == 'tex2d':
829                    line_seek += 3
830                    tex_name = common.line_trim(lines[line_seek])
831                    tex_path = common.line_trim(lines[line_seek + 1])
832                    tex_map = common.line_trim(lines[line_seek + 2]).split(' ')
833                    for map_datum in range(len(tex_map)):
834                        tex_map[map_datum] = float(tex_map[map_datum])
835                    mat_data.tex_list.append([prop_name, tex_name, tex_path, tex_map[:2], tex_map[2:]])
836                else:
837                    mat_data.tex_list.append([prop_name])
838
839                line_seek += 3
840
841            elif node_type == 'col':
842                prop_name = common.line_trim(lines[line_seek + 1])
843                tex_map = common.line_trim(lines[line_seek + 2]).split(' ')
844                for map_datum in range(len(tex_map)):
845                    tex_map[map_datum] = float(tex_map[map_datum])
846
847                mat_data.col_list.append([prop_name, tex_map[:]])
848                line_seek += 3
849
850            elif node_type == 'f':
851                prop_name = common.line_trim(lines[line_seek + 1])
852                val = float(common.line_trim(lines[line_seek + 2]))
853
854                mat_data.f_list.append([prop_name, val])
855                line_seek += 3
856            else:
857                raise Exception('未知の設定値タイプが見つかりました。')
858
859        return mat_data
@classmethod
def parse_json(cls, text):
861    @classmethod
862    def parse_json(cls, text):
863        import json
864        mat_data = Material()
865        mat_data.from_dict(json.loads(text))
866
867        return mat_data
@classmethod
def apply_to(cls, override, mate, mat_data, replace_tex=True):
869    @classmethod
870    def apply_to(cls, override, mate, mat_data, replace_tex=True):
871        mate['shader1'] = mat_data.shader1
872        mate['shader2'] = mat_data.shader2
873
874        if mate.use_nodes is False:
875            mate.use_nodes = True
876
877        nodes = mate.node_tree.nodes
878        nodes.clear()
879        # OUTPUT_MATERIAL, BSDF_PRINCIPLEDは消さない
880        #if len(nodes) > 2:
881        #    clear_nodes(nodes)
882
883        for tex_item in mat_data.tex_list:
884            prop_name = tex_item[0]
885
886            if len(tex_item) < 2:
887                common.create_tex(override, mate, prop_name)
888            else:
889                tex_name = tex_item[1]
890                tex_path = tex_item[2]
891                tex_map = tex_item[3] + tex_item[4]
892                common.create_tex(override, mate, prop_name, tex_name, tex_path, tex_path, tex_map, replace_tex)
893
894        for col_item in mat_data.col_list:
895            prop_name = col_item[0]
896            col = col_item[1]
897            common.create_col(override, mate, prop_name, col)
898
899        for item in mat_data.f_list:
900            prop_name = item[0]
901            f = item[1]
902            common.create_float(override, mate, prop_name, f)
903
904        for key, value in mat_data.custom_list.items():
905            mate[key] = value
906            if type(value) == bool:
907                rna_ui = mate.get('_RNA_UI', dict())
908                rna_ui[key] = {
909                    "default"  : value,
910                    "min"      : 0    ,
911                    "max"      : 1    ,
912                    "soft_min" : 0    ,
913                    "soft_max" : 1    ,
914                }
915                mate['_RNA_UI'] = rna_ui
916
917
918
919        align_nodes(mate)
@classmethod
def apply_to_old( cls, override, mate, mat_data, replace_tex=True, decorate=True, skip_same_prop=True):
 921    @classmethod
 922    def apply_to_old(cls, override, mate, mat_data, replace_tex=True, decorate=True, skip_same_prop=True):
 923        ob = override['active_object']
 924        me = ob.data
 925
 926        mate['shader1'] = mat_data.shader1
 927        mate['shader2'] = mat_data.shader2
 928
 929        slot_index = 0
 930        olds_slots = {}
 931        read_texes = set() if skip_same_prop else None
 932
 933        for item in mat_data.tex_list:
 934            prop_name = item[0]
 935            if read_texes:
 936                if prop_name in read_texes:
 937                    continue
 938                read_texes.add(prop_name)
 939
 940            slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'IMAGE')
 941            slot.use_rgb_to_intensity = False
 942            mate.use_textures[slot_index] = True
 943
 944            if len(item) > 4:
 945                tex = slot.texture
 946                tex_name = item[1]
 947                tex_path = item[2]
 948                if tex_name in read_texes:
 949                    continue
 950
 951                if tex_name != common.remove_serial_number(tex.image.name):
 952                    tex.image.name = tex_name
 953                tex.image['cm3d2_path'] = tex_path
 954                tex.image.filepath = tex.image['cm3d2_path']
 955
 956                slot.offset = item[3]
 957                slot.scale = item[4]
 958                if replace_tex:
 959                    if common.replace_cm3d2_tex(tex.image, reload_path=True) and prop_name == '_MainTex':
 960                        for face in me.polygons:
 961                            if face.material_index == ob.active_material_index:
 962                                me.uv_textures.active.data[face.index].image = tex.image
 963            slot_index += 1
 964
 965        for item in mat_data.col_list:
 966            prop_name = item[0]
 967            col = item[1]
 968
 969            slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND')
 970
 971            mate.use_textures[slot_index] = False
 972            slot.use_rgb_to_intensity = True
 973            slot.color = col[:3]
 974            slot.diffuse_color_factor = col[3]
 975
 976            slot_index += 1
 977
 978        for item in mat_data.f_list:
 979            prop_name = item[0]
 980            f = item[1]
 981
 982            slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND')
 983
 984            mate.use_textures[slot_index] = False
 985            slot.use_rgb_to_intensity = False
 986            slot.diffuse_color_factor = f
 987
 988            slot_index += 1
 989
 990        # 存在しないスロットをクリア
 991        for item_index in range(slot_index, len(mate.texture_slots)):
 992            if mate.texture_slots[item_index]:
 993                mate.texture_slots.clear(item_index)
 994
 995        # プレビューへの反映
 996        for slot in mate.texture_slots:
 997            if slot:
 998                common.set_texture_color(slot)
 999
1000        if decorate:
1001            common.decorate_material(mate, decorate, me, ob.active_material_index)
@staticmethod
def search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, tex_type):
1003    @staticmethod
1004    def search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, tex_type):
1005        tex = None
1006        slot_item = mate.texture_slots[slot_index]
1007        slot_name = slot_item.name if slot_item else ''
1008
1009        slot_name = common.remove_serial_number(slot_name)
1010        # 指定スロットが同名であればそのスロットをそのまま利用する
1011        if prop_name == slot_name:
1012            slot = slot_item
1013        else:
1014            # スロット名が異なり、既にスロットがある場合はキャッシュに格納
1015            if slot_item:
1016                olds_slots[slot_name] = slot_item
1017            slot = mate.texture_slots.create(slot_index)
1018
1019            if prop_name in olds_slots:
1020                tex = olds_slots.pop(prop_name).texture
1021            else:
1022                for item_index in range(slot_index + 1, len(mate.texture_slots)):
1023                    slot_item = mate.texture_slots[item_index]
1024                    if slot_item is None:
1025                        break
1026                    if prop_name == common.remove_serial_number(slot_item.name):
1027                        tex = slot_item.texture
1028                        break
1029            if tex is None:
1030                tex = override['blend_data'].textures.new(prop_name, tex_type)
1031            slot.texture = tex
1032        return slot
def clear_nodes(nodes):
1035def clear_nodes(nodes):
1036    for node in nodes:
1037        if node.type not in ['VALUE', 'RGB', 'TEX_IMAGE']:
1038            nodes.remove(node)
def align_nodes(mate):
1041def align_nodes(mate):
1042    nodes = mate.node_tree.nodes
1043    # Principled BSDFがある前提での整列
1044    bsdf = nodes.get('Principled BSDF')
1045    base_location = (10, 300)
1046    if bsdf:
1047        main_tex = nodes.get('_MainTex')
1048        if main_tex:
1049            mate.node_tree.links.new(bsdf.inputs['Base Color'], main_tex.outputs['Color'])
1050            mate.node_tree.links.new(bsdf.inputs['Alpha'], main_tex.outputs['Alpha'])
1051        shininess = nodes.get('_Shininess')
1052        if shininess:
1053            mate.node_tree.links.new(bsdf.inputs['Specular'], shininess.outputs[0])
1054        base_location = bsdf.location
1055
1056    shader_name = mate.get('shader1')
1057    if shader_name:
1058        location_x = base_location[0] - 400
1059        location_y = base_location[1] + 60
1060        shader_prop = MaterialHandler.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(shader_name)
1061        node_list = shader_prop.get('tex_list')
1062        if node_list:
1063            for node_name in node_list:
1064                node = nodes.get(node_name)
1065                if node:
1066                    node.location = (location_x, location_y)
1067                    node.hide = True
1068                    location_y -= 60
1069
1070        col_list = shader_prop.get('col_list')
1071        if col_list:
1072            for node_name in col_list:
1073                node = nodes.get(node_name)
1074                if node:
1075                    node.location = (location_x, location_y)
1076                    location_y -= 200
1077
1078        f_list = shader_prop.get('f_list')
1079        if f_list:
1080            for node_name in f_list:
1081                node = nodes.get(node_name)
1082                if node:
1083                    node.location = (location_x, location_y)
1084                    location_y -= 90